OLD | NEW |
1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
4 | 4 |
5 package memcache | 5 package memcache |
6 | 6 |
7 // Interface is the full interface to the memcache service. | 7 import ( |
| 8 » "github.com/luci/luci-go/common/errors" |
| 9 » "golang.org/x/net/context" |
| 10 ) |
| 11 |
| 12 func filterItems(lme errors.LazyMultiError, items []Item, nilErr error) ([]Item,
[]int) { |
| 13 » idxMap := make([]int, 0, len(items)) |
| 14 » retItems := make([]Item, 0, len(items)) |
| 15 » for i, itm := range items { |
| 16 » » if itm != nil { |
| 17 » » » idxMap = append(idxMap, i) |
| 18 » » » retItems = append(retItems, itm) |
| 19 » » } else { |
| 20 » » » lme.Assign(i, nilErr) |
| 21 » » } |
| 22 » } |
| 23 » return retItems, idxMap |
| 24 } |
| 25 |
| 26 func multiCall(items []Item, nilErr error, inner func(items []Item, cb RawCB) er
ror) error { |
| 27 » lme := errors.NewLazyMultiError(len(items)) |
| 28 » realItems, idxMap := filterItems(lme, items, nilErr) |
| 29 » j := 0 |
| 30 » err := inner(realItems, func(err error) { |
| 31 » » lme.Assign(idxMap[j], err) |
| 32 » » j++ |
| 33 » }) |
| 34 » if err == nil { |
| 35 » » err = lme.Get() |
| 36 » » if len(items) == 1 { |
| 37 » » » err = errors.SingleError(err) |
| 38 » » } |
| 39 » } |
| 40 » return err |
| 41 } |
| 42 |
| 43 // NewItem creates a new, mutable, memcache item. |
| 44 func NewItem(c context.Context, key string) Item { |
| 45 » return Raw(c).NewItem(key) |
| 46 } |
| 47 |
| 48 // Add writes items to memcache iff they don't already exist. |
8 // | 49 // |
9 // The *Multi methods may return a "github.com/luci/luci-go/common/errors".Multi
Error | 50 // If only one item is provided its error will be returned directly. If more |
10 // if the rpc to the server was successful, but, e.g. some of the items were | 51 // than one item is provided, an errors.MultiError will be returned in the |
11 // missing. They may also return a regular error, if, for example, the rpc | 52 // event of an error, with a given error index corresponding to the error |
12 // failed outright. | 53 // encountered when processing the item at that index. |
13 type Interface interface { | 54 func Add(c context.Context, items ...Item) error { |
14 » // NewItem creates a new, mutable, memcache item. | 55 » return multiCall(items, ErrNotStored, Raw(c).AddMulti) |
15 » NewItem(key string) Item | 56 } |
16 | 57 |
17 » // Add puts a single item into memcache, but only if it didn't exist in | 58 // Set writes items into memcache unconditionally. |
18 » // memcache before. | 59 // |
19 » Add(item Item) error | 60 // If only one item is provided its error will be returned directly. If more |
| 61 // than one item is provided, an errors.MultiError will be returned in the |
| 62 // event of an error, with a given error index corresponding to the error |
| 63 // encountered when processing the item at that index. |
| 64 func Set(c context.Context, items ...Item) error { |
| 65 » return multiCall(items, ErrNotStored, Raw(c).SetMulti) |
| 66 } |
20 | 67 |
21 » // Set the item in memcache, whether or not it exists. | 68 func getMultiImpl(raw RawInterface, items []Item) error { |
22 » Set(item Item) error | 69 » lme := errors.NewLazyMultiError(len(items)) |
| 70 » realItems, idxMap := filterItems(lme, items, ErrCacheMiss) |
| 71 » if len(realItems) == 0 { |
| 72 » » return lme.Get() |
| 73 » } |
23 | 74 |
24 » // Get retrieves an item from memcache. | 75 » keys := make([]string, len(realItems)) |
25 » // | 76 » for i, itm := range realItems { |
26 » // On a cache miss ErrCacheMiss will be returned. Item will always be | 77 » » keys[i] = itm.Key() |
27 » // returned, even on a miss, but it's value may be empty if it was a mis
s. | 78 » } |
28 » Get(key string) (Item, error) | |
29 | 79 |
30 » // Delete removes an item from memcache. | 80 » j := 0 |
31 » Delete(key string) error | 81 » err := raw.GetMulti(keys, func(item Item, err error) { |
| 82 » » i := idxMap[j] |
| 83 » » if !lme.Assign(i, err) { |
| 84 » » » items[i].SetAll(item) |
| 85 » » } |
| 86 » » j++ |
| 87 » }) |
| 88 » if err == nil { |
| 89 » » err = lme.Get() |
| 90 » » if len(items) == 1 { |
| 91 » » » err = errors.SingleError(err) |
| 92 » » } |
| 93 » } |
| 94 » return err |
| 95 } |
32 | 96 |
33 » // CompareAndSwap accepts an item which is the result of Get() or GetMul
ti(). | 97 // Get retrieves items from memcache. |
34 » // The Get functions add a secret field to item ('CasID'), which is used
as | 98 func Get(c context.Context, items ...Item) error { |
35 » // the "compare" value for the "CompareAndSwap". The actual "Value" fiel
d of | 99 » return getMultiImpl(Raw(c), items) |
36 » // the object set by the Get functions is the "swap" value. | 100 } |
37 » // | |
38 » // Example: | |
39 » // mc := memcache.Get(context) | |
40 » // itm := mc.NewItem("aKey") | |
41 » // mc.Get(itm) // check error | |
42 » // itm.SetValue(append(itm.Value(), []byte("more bytes"))) | |
43 » // mc.CompareAndSwap(itm) // check error | |
44 » CompareAndSwap(item Item) error | |
45 | 101 |
46 » // Batch operations; GetMulti takes a []Item instead of []string to impr
ove | 102 // GetKey is a convenience method for generating and retrieving an Item instance |
47 » // ergonomics when streamlining these operations. | 103 // for the specified from memcache key. |
48 » AddMulti(items []Item) error | 104 // |
49 » SetMulti(items []Item) error | 105 // On a cache miss ErrCacheMiss will be returned. Item will always be |
50 » GetMulti(items []Item) error | 106 // returned, even on a miss, but it's value may be empty if it was a miss. |
51 » DeleteMulti(keys []string) error | 107 func GetKey(c context.Context, key string) (Item, error) { |
52 » CompareAndSwapMulti(items []Item) error | 108 » raw := Raw(c) |
| 109 » ret := raw.NewItem(key) |
| 110 » err := getMultiImpl(raw, []Item{ret}) |
| 111 » return ret, err |
| 112 } |
53 | 113 |
54 » // Increment adds delta to the uint64 contained at key. If the memcache
key | 114 // Delete deletes items from memcache. |
55 » // is missing, it's populated with initialValue before applying delta (i
.e. | 115 // |
56 » // the final value would be initialValue+delta). | 116 // If only one item is provided its error will be returned directly. If more |
57 » // | 117 // than one item is provided, an errors.MultiError will be returned in the |
58 » // Underflow caps at 0, overflow wraps back to 0. | 118 // event of an error, with a given error index corresponding to the error |
59 » // | 119 // encountered when processing the item at that index. |
60 » // If key contains a value which is not exactly 8 bytes, it's assumed to | 120 func Delete(c context.Context, keys ...string) error { |
61 » // contain non-number data and this method will return an error. | 121 » lme := errors.NewLazyMultiError(len(keys)) |
62 » Increment(key string, delta int64, initialValue uint64) (newValue uint64
, err error) | 122 » i := 0 |
| 123 » err := Raw(c).DeleteMulti(keys, func(err error) { |
| 124 » » lme.Assign(i, err) |
| 125 » » i++ |
| 126 » }) |
| 127 » if err == nil { |
| 128 » » err = lme.Get() |
| 129 » » if len(keys) == 1 { |
| 130 » » » err = errors.SingleError(err) |
| 131 » » } |
| 132 » } |
| 133 » return err |
| 134 } |
63 | 135 |
64 » // IncrementExisting is like Increment, except that the valu must exist | 136 // CompareAndSwap writes the given item that was previously returned by Get, if |
65 » // already. | 137 // the value was neither modified or evicted between the Get and the |
66 » IncrementExisting(key string, delta int64) (newValue uint64, err error) | 138 // CompareAndSwap calls. |
| 139 // |
| 140 // Example: |
| 141 // itm := memcache.NewItem(context, "aKey") |
| 142 // memcache.Get(context, itm) // check error |
| 143 // itm.SetValue(append(itm.Value(), []byte("more bytes"))) |
| 144 // memcache.CompareAndSwap(context, itm) // check error |
| 145 // |
| 146 // If only one item is provided its error will be returned directly. If more |
| 147 // than one item is provided, an errors.MultiError will be returned in the |
| 148 // event of an error, with a given error index corresponding to the error |
| 149 // encountered when processing the item at that index. |
| 150 func CompareAndSwap(c context.Context, items ...Item) error { |
| 151 » return multiCall(items, ErrNotStored, Raw(c).CompareAndSwapMulti) |
| 152 } |
67 | 153 |
68 » // Flush dumps the entire memcache state. | 154 // Increment adds delta to the uint64 contained at key. If the memcache key |
69 » Flush() error | 155 // is missing, it's populated with initialValue before applying delta (i.e. |
| 156 // the final value would be initialValue+delta). |
| 157 // |
| 158 // Underflow caps at 0, overflow wraps back to 0. |
| 159 // |
| 160 // If key contains a value which is not exactly 8 bytes, it's assumed to |
| 161 // contain non-number data and this method will return an error. |
| 162 func Increment(c context.Context, key string, delta int64, initialValue uint64)
(uint64, error) { |
| 163 » return Raw(c).Increment(key, delta, &initialValue) |
| 164 } |
70 | 165 |
71 » // Stats gets some best-effort statistics about the current state of mem
cache. | 166 // IncrementExisting is like Increment, except that the valu must exist |
72 » Stats() (*Statistics, error) | 167 // already. |
| 168 func IncrementExisting(c context.Context, key string, delta int64) (uint64, erro
r) { |
| 169 » return Raw(c).Increment(key, delta, nil) |
| 170 } |
73 | 171 |
74 » Raw() RawInterface | 172 // Flush dumps the entire memcache state. |
| 173 func Flush(c context.Context) error { |
| 174 » return Raw(c).Flush() |
75 } | 175 } |
| 176 |
| 177 // Stats gets some best-effort statistics about the current state of memcache. |
| 178 func Stats(c context.Context) (*Statistics, error) { |
| 179 return Raw(c).Stats() |
| 180 } |
OLD | NEW |