Chromium Code Reviews| 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 { | 
| 
 
dnj
2016/09/01 15:25:40
(This is the "single argument, single error" logic
 
 | 
| + 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 { | 
| 
 
dnj
2016/09/01 15:25:40
Big change here is that I axed this Interface and
 
 | 
| - // 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() | 
| } |