Index: service/memcache/interface.go |
diff --git a/service/memcache/interface.go b/service/memcache/interface.go |
index 6d4d33b2d880660f5b499adce34836c6d57bb7e1..5167667587da615f095f0827977a3dd127969776 100644 |
--- a/service/memcache/interface.go |
+++ b/service/memcache/interface.go |
@@ -4,72 +4,177 @@ |
package memcache |
-// Interface is the full interface to the memcache service. |
+import ( |
+ "github.com/luci/luci-go/common/errors" |
+ "golang.org/x/net/context" |
+) |
+ |
+func filterItems(lme errors.LazyMultiError, items []Item, nilErr error) ([]Item, []int) { |
+ idxMap := make([]int, 0, len(items)) |
+ retItems := make([]Item, 0, len(items)) |
+ for i, itm := range items { |
+ if itm != nil { |
+ idxMap = append(idxMap, i) |
+ retItems = append(retItems, itm) |
+ } else { |
+ lme.Assign(i, nilErr) |
+ } |
+ } |
+ return retItems, idxMap |
+} |
+ |
+func multiCall(items []Item, nilErr error, inner func(items []Item, cb RawCB) error) error { |
+ lme := errors.NewLazyMultiError(len(items)) |
+ realItems, idxMap := filterItems(lme, items, nilErr) |
+ j := 0 |
+ err := inner(realItems, func(err error) { |
+ lme.Assign(idxMap[j], err) |
+ j++ |
+ }) |
+ if err == nil { |
+ err = lme.Get() |
+ if len(items) == 1 { |
+ err = errors.SingleError(err) |
+ } |
+ } |
+ return err |
+} |
+ |
+// NewItem creates a new, mutable, memcache item. |
+func NewItem(c context.Context, key string) Item { |
+ return Raw(c).NewItem(key) |
+} |
+ |
+// Add writes items to memcache iff they don't already exist. |
+// |
+// If only one item is provided its error will be returned directly. If more |
+// than one item is provided, an errors.MultiError will be returned in the |
+// event of an error, with a given error index corresponding to the error |
+// encountered when processing the item at that index. |
+func Add(c context.Context, items ...Item) error { |
+ return multiCall(items, ErrNotStored, Raw(c).AddMulti) |
+} |
+ |
+// Set writes items into memcache unconditionally. |
+// |
+// If only one item is provided its error will be returned directly. If more |
+// than one item is provided, an errors.MultiError will be returned in the |
+// event of an error, with a given error index corresponding to the error |
+// encountered when processing the item at that index. |
+func Set(c context.Context, items ...Item) error { |
+ return multiCall(items, ErrNotStored, Raw(c).SetMulti) |
+} |
+ |
+func getMultiImpl(raw RawInterface, items []Item) error { |
+ lme := errors.NewLazyMultiError(len(items)) |
+ realItems, idxMap := filterItems(lme, items, ErrCacheMiss) |
+ if len(realItems) == 0 { |
+ return lme.Get() |
+ } |
+ |
+ keys := make([]string, len(realItems)) |
+ for i, itm := range realItems { |
+ keys[i] = itm.Key() |
+ } |
+ |
+ j := 0 |
+ err := raw.GetMulti(keys, func(item Item, err error) { |
+ i := idxMap[j] |
+ if !lme.Assign(i, err) { |
+ items[i].SetAll(item) |
+ } |
+ j++ |
+ }) |
+ if err == nil { |
+ err = lme.Get() |
+ if len(items) == 1 { |
+ err = errors.SingleError(err) |
+ } |
+ } |
+ return err |
+} |
+ |
+// Get retrieves items from memcache. |
+func Get(c context.Context, items ...Item) error { |
+ return getMultiImpl(Raw(c), items) |
+} |
+ |
+// GetKey is a convenience method for generating and retrieving an Item instance |
+// for the specified from memcache key. |
+// |
+// On a cache miss ErrCacheMiss will be returned. Item will always be |
+// returned, even on a miss, but it's value may be empty if it was a miss. |
+func GetKey(c context.Context, key string) (Item, error) { |
+ raw := Raw(c) |
+ ret := raw.NewItem(key) |
+ err := getMultiImpl(raw, []Item{ret}) |
+ return ret, err |
+} |
+ |
+// Delete deletes items from memcache. |
+// |
+// If only one item is provided its error will be returned directly. If more |
+// than one item is provided, an errors.MultiError will be returned in the |
+// event of an error, with a given error index corresponding to the error |
+// encountered when processing the item at that index. |
+func Delete(c context.Context, keys ...string) error { |
+ lme := errors.NewLazyMultiError(len(keys)) |
+ i := 0 |
+ err := Raw(c).DeleteMulti(keys, func(err error) { |
+ lme.Assign(i, err) |
+ i++ |
+ }) |
+ if err == nil { |
+ err = lme.Get() |
+ if len(keys) == 1 { |
+ err = errors.SingleError(err) |
+ } |
+ } |
+ return err |
+} |
+ |
+// CompareAndSwap writes the given item that was previously returned by Get, if |
+// the value was neither modified or evicted between the Get and the |
+// CompareAndSwap calls. |
+// |
+// Example: |
+// itm := memcache.NewItem(context, "aKey") |
+// memcache.Get(context, itm) // check error |
+// itm.SetValue(append(itm.Value(), []byte("more bytes"))) |
+// memcache.CompareAndSwap(context, itm) // check error |
+// |
+// If only one item is provided its error will be returned directly. If more |
+// than one item is provided, an errors.MultiError will be returned in the |
+// event of an error, with a given error index corresponding to the error |
+// encountered when processing the item at that index. |
+func CompareAndSwap(c context.Context, items ...Item) error { |
+ return multiCall(items, ErrNotStored, Raw(c).CompareAndSwapMulti) |
+} |
+ |
+// Increment adds delta to the uint64 contained at key. If the memcache key |
+// is missing, it's populated with initialValue before applying delta (i.e. |
+// the final value would be initialValue+delta). |
+// |
+// Underflow caps at 0, overflow wraps back to 0. |
// |
-// The *Multi methods may return a "github.com/luci/luci-go/common/errors".MultiError |
-// if the rpc to the server was successful, but, e.g. some of the items were |
-// missing. They may also return a regular error, if, for example, the rpc |
-// failed outright. |
-type Interface interface { |
- // NewItem creates a new, mutable, memcache item. |
- NewItem(key string) Item |
- |
- // Add puts a single item into memcache, but only if it didn't exist in |
- // memcache before. |
- Add(item Item) error |
- |
- // Set the item in memcache, whether or not it exists. |
- Set(item Item) error |
- |
- // Get retrieves an item from memcache. |
- // |
- // On a cache miss ErrCacheMiss will be returned. Item will always be |
- // returned, even on a miss, but it's value may be empty if it was a miss. |
- Get(key string) (Item, error) |
- |
- // Delete removes an item from memcache. |
- Delete(key string) error |
- |
- // CompareAndSwap accepts an item which is the result of Get() or GetMulti(). |
- // The Get functions add a secret field to item ('CasID'), which is used as |
- // the "compare" value for the "CompareAndSwap". The actual "Value" field of |
- // the object set by the Get functions is the "swap" value. |
- // |
- // Example: |
- // mc := memcache.Get(context) |
- // itm := mc.NewItem("aKey") |
- // mc.Get(itm) // check error |
- // itm.SetValue(append(itm.Value(), []byte("more bytes"))) |
- // mc.CompareAndSwap(itm) // check error |
- CompareAndSwap(item Item) error |
- |
- // Batch operations; GetMulti takes a []Item instead of []string to improve |
- // ergonomics when streamlining these operations. |
- AddMulti(items []Item) error |
- SetMulti(items []Item) error |
- GetMulti(items []Item) error |
- DeleteMulti(keys []string) error |
- CompareAndSwapMulti(items []Item) error |
- |
- // Increment adds delta to the uint64 contained at key. If the memcache key |
- // is missing, it's populated with initialValue before applying delta (i.e. |
- // the final value would be initialValue+delta). |
- // |
- // Underflow caps at 0, overflow wraps back to 0. |
- // |
- // If key contains a value which is not exactly 8 bytes, it's assumed to |
- // contain non-number data and this method will return an error. |
- Increment(key string, delta int64, initialValue uint64) (newValue uint64, err error) |
- |
- // IncrementExisting is like Increment, except that the valu must exist |
- // already. |
- IncrementExisting(key string, delta int64) (newValue uint64, err error) |
- |
- // Flush dumps the entire memcache state. |
- Flush() error |
- |
- // Stats gets some best-effort statistics about the current state of memcache. |
- Stats() (*Statistics, error) |
- |
- Raw() RawInterface |
+// If key contains a value which is not exactly 8 bytes, it's assumed to |
+// contain non-number data and this method will return an error. |
+func Increment(c context.Context, key string, delta int64, initialValue uint64) (uint64, error) { |
+ return Raw(c).Increment(key, delta, &initialValue) |
+} |
+ |
+// IncrementExisting is like Increment, except that the valu must exist |
+// already. |
+func IncrementExisting(c context.Context, key string, delta int64) (uint64, error) { |
+ return Raw(c).Increment(key, delta, nil) |
+} |
+ |
+// Flush dumps the entire memcache state. |
+func Flush(c context.Context) error { |
+ return Raw(c).Flush() |
+} |
+ |
+// Stats gets some best-effort statistics about the current state of memcache. |
+func Stats(c context.Context) (*Statistics, error) { |
+ return Raw(c).Stats() |
} |