Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(616)

Side by Side Diff: impl/cloud/memcache_test.go

Issue 2513253002: impl/cloud: Add support for "memcached" memcache. (Closed)
Patch Set: Namespace all keys to luci/gae/impl/cloud. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« impl/cloud/memcache.go ('K') | « impl/cloud/memcache.go ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file.
4
5 package cloud
6
7 import (
8 "flag"
9 "fmt"
10 "math"
11 "testing"
12 "time"
13
14 "github.com/luci/luci-go/common/errors"
15
16 "github.com/luci/gae/service/info"
17 mc "github.com/luci/gae/service/memcache"
18
19 "github.com/bradfitz/gomemcache/memcache"
20 "golang.org/x/net/context"
21
22 . "github.com/smartystreets/goconvey/convey"
23 )
24
25 var memcacheServer = flag.String("test.memcache-server", "",
26 "[<addr>]:<port> of memcached service to test against. THIS WILL FLUSH T HE CACHE.")
27
28 // TestMemcache tests the memcache implementation against a live "memcached"
29 // instance. The test assumes ownership of the instance, and will flush all of
30 // its keys in between test suites, so DO NOT connect this to a production
31 // memcached cluster!
32 //
33 // The memcache host is passed to this test suite via the "-test.memcache-host"
34 // flag. If the flag is not provided, this test suite will be skipped.
35 //
36 // Starting a local memcached server (on default port 11211) can be done with:
37 // $ memcached -l localhost -vvv
38 //
39 // Testing against this service can be done using the flag:
40 // "-test.memcache-server localhost:11211
41 func TestMemcache(t *testing.T) {
42 t.Parallel()
43
44 // See if a memcache server is configured. If no server is configured, w e will
45 // skip this test suite.
46 if *memcacheServer == "" {
47 t.Logf("No memcache server detected (-test.memcache-server). Ski pping test suite.")
48 return
49 }
50
51 Convey(fmt.Sprintf(`A memcache instance bound to %q`, *memcacheServer), t, func() {
52 client := memcache.New(*memcacheServer)
53 if err := client.DeleteAll(); err != nil {
54 t.Fatalf("failed to flush memcache before running test s uite: %s", err)
55 }
56
57 cfg := Config{MC: client}
58 c := cfg.Use(context.Background())
59
60 get := func(c context.Context, keys ...string) []string {
61 bmc := bindMemcacheClient(nil, info.GetNamespace(c))
62
63 v := make([]string, len(keys))
64 for i, k := range keys {
65 itm, err := client.Get(bmc.makeKey(k))
66 switch err {
67 case nil:
68 v[i] = string(itm.Value)
69
70 case memcache.ErrCacheMiss:
71 break
72
73 default:
74 t.Fatalf("item %q could not be loaded: % s", itm.Key, err)
75 }
76 }
77 return v
78 }
79
80 Convey(`AddMulti`, func() {
81 items := []mc.Item{
82 mc.NewItem(c, "foo").SetValue([]byte("FOO")),
83 mc.NewItem(c, "bar").SetValue([]byte("BAR")),
84 mc.NewItem(c, "baz").SetValue([]byte("BAZ")),
85 }
86 So(mc.Add(c, items...), ShouldBeNil)
87 So(get(c, "foo", "bar", "baz"), ShouldResemble, []string {"FOO", "BAR", "BAZ"})
88
89 // Namespaced.
90 oc := info.MustNamespace(c, "other")
91 oitems := make([]mc.Item, len(items))
92 for i, itm := range items {
93 oitems[i] = mc.NewItem(oc, itm.Key())
94 oitems[i].SetValue([]byte("OTHER_" + string(itm. Value())))
95 }
96 So(mc.Add(oc, oitems...), ShouldBeNil)
97 So(get(oc, "foo", "bar", "baz"), ShouldResemble, []strin g{"OTHER_FOO", "OTHER_BAR", "OTHER_BAZ"})
98
99 Convey(`Returns ErrNotStored if the item is already adde d.`, func() {
100 So(mc.Add(c, items...), ShouldResemble, errors.M ultiError{
101 mc.ErrNotStored, mc.ErrNotStored, mc.Err NotStored})
102 })
103
104 Convey(`GetMulti`, func() {
105 getItems := []mc.Item{
106 mc.NewItem(c, "foo"),
107 mc.NewItem(c, "bar"),
108 mc.NewItem(c, "baz"),
109 }
110 So(mc.Get(c, getItems...), ShouldBeNil)
111 So(getItems[0].Value(), ShouldResemble, []byte(" FOO"))
112 So(getItems[1].Value(), ShouldResemble, []byte(" BAR"))
113 So(getItems[2].Value(), ShouldResemble, []byte(" BAZ"))
114
115 // Namespaced.
116 So(mc.Get(oc, getItems...), ShouldBeNil)
117 So(oitems[0].Value(), ShouldResemble, []byte("OT HER_FOO"))
118 So(oitems[1].Value(), ShouldResemble, []byte("OT HER_BAR"))
119 So(oitems[2].Value(), ShouldResemble, []byte("OT HER_BAZ"))
120 })
121
122 Convey(`SetMulti`, func() {
123 items[2].SetValue([]byte("NEWBAZ"))
124 items = append(items, mc.NewItem(c, "qux").SetVa lue([]byte("QUX")))
125
126 oitems[2].SetValue([]byte("OTHER_NEWBAZ"))
127 oitems = append(oitems, mc.NewItem(c, "qux").Set Value([]byte("OTHER_QUX")))
128
129 So(mc.Set(c, items...), ShouldBeNil)
130 So(mc.Set(oc, oitems...), ShouldBeNil)
131
132 So(get(c, "foo", "bar", "baz", "qux"), ShouldRes emble, []string{"FOO", "BAR", "NEWBAZ", "QUX"})
133 So(get(oc, "foo", "bar", "baz", "qux"), ShouldRe semble,
134 []string{"OTHER_FOO", "OTHER_BAR", "OTHE R_NEWBAZ", "OTHER_QUX"})
135 })
136
137 Convey(`DeleteMulti`, func() {
138 So(mc.Delete(c, "foo", "bar"), ShouldBeNil)
139 So(get(c, "foo", "bar", "baz"), ShouldResemble, []string{"", "", "BAZ"})
140
141 So(mc.Delete(oc, "foo", "bar"), ShouldBeNil)
142 So(get(oc, "foo", "bar", "baz"), ShouldResemble, []string{"", "", "OTHER_BAZ"})
143 })
144
145 Convey(`CompareAndSwapMulti`, func() {
146 // Get all of the values.
147 So(mc.Get(c, items...), ShouldBeNil)
148
149 // Change "foo"'s value.
150 newFoo := mc.NewItem(c, "foo")
151 newFoo.SetAll(items[0])
152 newFoo.SetValue([]byte("NEWFOO"))
153 So(mc.Set(c, newFoo), ShouldBeNil)
154
155 Convey(`Works`, func() {
156 // Do CAS on foo, bar. "foo" should fail because it's changed.
157 items[0].SetValue([]byte("CASFOO"))
158 items[1].SetValue([]byte("CASBAR"))
159 So(mc.CompareAndSwap(c, items...), Shoul dResemble, errors.MultiError{mc.ErrCASConflict, nil, nil})
160
161 So(get(c, "foo", "bar", "baz"), ShouldRe semble, []string{"NEWFOO", "CASBAR", "BAZ"})
162 })
163
164 Convey(`SetAll transfers CAS ID`, func() {
165 // Do CAS on foo, bar. "foo" should fail because it's changed.
166 foo := mc.NewItem(c, "foo")
167 foo.SetAll(items[0])
168 foo.SetValue([]byte("CASFOO"))
169
170 bar := mc.NewItem(c, "bar")
171 bar.SetAll(items[1])
172 bar.SetValue([]byte("CASBAR"))
173
174 So(mc.CompareAndSwap(c, foo, bar), Shoul dResemble, errors.MultiError{mc.ErrCASConflict, nil})
175
176 So(get(c, "foo", "bar"), ShouldResemble, []string{"NEWFOO", "CASBAR"})
177 })
178 })
179
180 Convey(`Flush`, func() {
181 So(mc.Flush(c), ShouldBeNil)
182 So(get(c, "foo", "bar", "baz"), ShouldResemble, []string{"", "", ""})
183 })
184 })
185
186 Convey(`Increment`, func() {
187
188 Convey(`Missing`, func() {
189 // IncrementExisting on non-existent item will r eturn ErrCacheMiss.
190 _, err := mc.IncrementExisting(c, "foo", 1)
191 So(err, ShouldEqual, mc.ErrCacheMiss)
192
193 // IncrementExisting with delta of 0 on non-exis tent item will return
194 // ErrCacheMiss.
195 _, err = mc.IncrementExisting(c, "foo", 1)
196 So(err, ShouldEqual, mc.ErrCacheMiss)
197 })
198
199 Convey(`Initial value (positive)`, func() {
200 // Can set the initial value.
201 nv, err := mc.Increment(c, "foo", 1, 1336)
202 So(err, ShouldBeNil)
203 So(nv, ShouldEqual, 1337)
204 So(get(c, "foo"), ShouldResemble, []string{"1337 "})
205
206 // Followup increment (initial value is ignored) .
207 nv, err = mc.Increment(c, "foo", 1, 20000)
208 So(err, ShouldBeNil)
209 So(nv, ShouldEqual, 1338)
210 })
211
212 Convey(`Initial value (positive, overflows)`, func() {
213 // Can set the initial value.
214 nv, err := mc.Increment(c, "foo", 10, math.MaxUi nt64)
215 So(err, ShouldBeNil)
216 So(nv, ShouldEqual, 9)
217 So(get(c, "foo"), ShouldResemble, []string{"9"})
218 })
219
220 Convey(`Initial value (negative)`, func() {
221 // Can set the initial value.
222 nv, err := mc.Increment(c, "foo", -1, 1338)
223 So(err, ShouldBeNil)
224 So(nv, ShouldEqual, 1337)
225 So(get(c, "foo"), ShouldResemble, []string{"1337 "})
226 })
227
228 Convey(`Initial value (negative, underflows to zero)`, f unc() {
229 // Can set the initial value.
230 nv, err := mc.Increment(c, "foo", -1337, 2)
231 So(err, ShouldBeNil)
232 So(nv, ShouldEqual, 0)
233 So(get(c, "foo"), ShouldResemble, []string{"0"})
234 })
235
236 Convey(`With large initial value (zero delta)`, func() {
237 const iv = uint64(math.MaxUint64 - 1)
238
239 // Can set the initial value.
240 nv, err := mc.Increment(c, "foo", 0, iv)
241 So(err, ShouldBeNil)
242 So(nv, ShouldEqual, uint64(math.MaxUint64-1))
243
244 Convey(`Can increment`, func() {
245 nv, err := mc.IncrementExisting(c, "foo" , 1)
246 So(err, ShouldBeNil)
247 So(nv, ShouldEqual, iv+1)
248 })
249
250 Convey(`Can increment (overflow, wraps)`, func() {
251 nv, err := mc.IncrementExisting(c, "foo" , 10)
252 So(err, ShouldBeNil)
253 So(nv, ShouldEqual, 8)
254 })
255
256 Convey(`Can decrement`, func() {
257 nv, err := mc.IncrementExisting(c, "foo" , -123)
258 So(err, ShouldBeNil)
259 So(nv, ShouldEqual, iv-123)
260 })
261 })
262
263 Convey(`With small initial value (zero delta)`, func() {
264 // Can set the initial value.
265 nv, err := mc.Increment(c, "foo", 0, 1337)
266 So(err, ShouldBeNil)
267 So(nv, ShouldEqual, 1337)
268
269 Convey(`Can decrement (underflow to zero)`, func () {
270 nv, err := mc.IncrementExisting(c, "foo" , -2000)
271 So(err, ShouldBeNil)
272 So(nv, ShouldEqual, 0)
273 })
274 })
275
276 Convey(`Namespaced`, func() {
277 oc := info.MustNamespace(c, "other")
278
279 // Initial (non-namespaced).
280 nv, err := mc.Increment(c, "foo", 1, 100)
281 So(err, ShouldBeNil)
282 So(nv, ShouldEqual, 101)
283
284 // Initial (namespaced).
285 _, err = mc.IncrementExisting(oc, "foo", -1)
286 So(err, ShouldEqual, mc.ErrCacheMiss)
287
288 nv, err = mc.Increment(oc, "foo", -1, 20000)
289 So(err, ShouldBeNil)
290 So(nv, ShouldEqual, 19999)
291
292 // Increment (namespaced).
293 nv, err = mc.IncrementExisting(oc, "foo", 1)
294 So(err, ShouldBeNil)
295 So(nv, ShouldEqual, 20000)
296
297 // Increment (non-namespaced).
298 nv, err = mc.IncrementExisting(c, "foo", 1)
299 So(err, ShouldBeNil)
300 So(nv, ShouldEqual, 102)
301 })
302 })
303
304 Convey(`Stats returns ErrNoStats`, func() {
305 _, err := mc.Stats(c)
306 So(err, ShouldEqual, mc.ErrNoStats)
307 })
308
309 Convey(`Items will expire (sleeps 1 second).`, func() {
310 item := mc.NewItem(c, "foo").SetValue([]byte("FOO")).Set Expiration(1 * time.Second)
311 So(mc.Add(c, item), ShouldBeNil)
312
313 // Immediate Get.
314 So(mc.Get(c, item), ShouldBeNil)
315 So(item.Value(), ShouldResemble, []byte("FOO"))
316
317 // Expire.
318 time.Sleep(1 * time.Second)
319 So(mc.Get(c, item), ShouldEqual, mc.ErrCacheMiss)
320 })
321 })
322 }
OLDNEW
« impl/cloud/memcache.go ('K') | « impl/cloud/memcache.go ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698