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

Side by Side Diff: appengine/memlock/memlock_test.go

Issue 1399533003: Move appengine/memlock from infra.git (Closed) Base URL: https://chromium.googlesource.com/external/github.com/luci/luci-go@add_meta
Patch Set: Created 5 years, 2 months 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
« no previous file with comments | « appengine/memlock/memlock.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 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package memlock
6
7 import (
8 "fmt"
9 "sync"
10 "testing"
11 "time"
12
13 "github.com/luci/gae/filter/featureBreaker"
14 "github.com/luci/gae/impl/memory"
15 "github.com/luci/gae/service/memcache"
16 "github.com/luci/luci-go/common/clock"
17 "github.com/luci/luci-go/common/clock/testclock"
18 . "github.com/smartystreets/goconvey/convey"
19 "golang.org/x/net/context"
20 )
21
22 type getBlockerFilter struct {
23 memcache.RawInterface
24 sync.Mutex
25
26 dropAll bool
27 }
28
29 func (f *getBlockerFilter) GetMulti(keys []string, cb memcache.RawItemCB) error {
30 f.Lock()
31 defer f.Unlock()
32 if f.dropAll {
33 for _, key := range keys {
34 cb(f.NewItem(key), nil)
35 }
36 return nil
37 }
38 return f.RawInterface.GetMulti(keys, cb)
39 }
40
41 func TestSimple(t *testing.T) {
42 // TODO(riannucci): Mock time.After so that we don't have to delay for r eal.
43
44 const key = memlockKeyPrefix + "testkey"
45
46 Convey("basic locking", t, func() {
47 start := time.Date(1986, time.October, 26, 1, 20, 00, 00, time.U TC)
48 ctx, clk := testclock.UseTime(context.Background(), start)
49 blocker := make(chan struct{})
50 clk.SetTimerCallback(func(time.Duration, clock.Timer) {
51 clk.Add(delay)
52 select {
53 case blocker <- struct{}{}:
54 default:
55 }
56 })
57
58 waitFalse := func(ctx context.Context) {
59 loop:
60 for {
61 select {
62 case <-blocker:
63 continue
64 case <-ctx.Done():
65 break loop
66 }
67 }
68 }
69
70 ctx, fb := featureBreaker.FilterMC(memory.Use(ctx), nil)
71 mc := memcache.Get(ctx)
72
73 Convey("fails to acquire when memcache is down", func() {
74 fb.BreakFeatures(nil, "AddMulti")
75 err := TryWithLock(ctx, "testkey", "id", func(context.Co ntext) error {
76 // should never reach here
77 So(false, ShouldBeTrue)
78 return nil
79 })
80 So(err, ShouldEqual, ErrFailedToLock)
81 })
82
83 Convey("returns the inner error", func() {
84 toRet := fmt.Errorf("sup")
85 err := TryWithLock(ctx, "testkey", "id", func(context.Co ntext) error {
86 return toRet
87 })
88 So(err, ShouldEqual, toRet)
89 })
90
91 Convey("returns the error", func() {
92 toRet := fmt.Errorf("sup")
93 err := TryWithLock(ctx, "testkey", "id", func(context.Co ntext) error {
94 return toRet
95 })
96 So(err, ShouldEqual, toRet)
97 })
98
99 Convey("can acquire when empty", func() {
100 err := TryWithLock(ctx, "testkey", "id", func(ctx contex t.Context) error {
101 isDone := func() bool {
102 select {
103 case <-ctx.Done():
104 return true
105 default:
106 return false
107 }
108 }
109
110 So(isDone(), ShouldBeFalse)
111
112 Convey("waiting for a while keeps refreshing the lock", func() {
113 // simulate waiting for 64*delay time, a nd ensuring that checkLoop
114 // runs that many times.
115 for i := 0; i < 64; i++ {
116 <-blocker
117 clk.Add(delay)
118 }
119 So(isDone(), ShouldBeFalse)
120 })
121
122 Convey("but sometimes we might lose it", func() {
123 Convey("because it was evicted", func() {
124 mc.Delete(key)
125 clk.Add(memcacheLockTime)
126 waitFalse(ctx)
127 })
128
129 Convey("or because of service issues", f unc() {
130 fb.BreakFeatures(nil, "CompareAn dSwapMulti")
131 waitFalse(ctx)
132 })
133 })
134 return nil
135 })
136 So(err, ShouldBeNil)
137 })
138
139 Convey("can lose it when it gets stolen", func() {
140 gbf := &getBlockerFilter{}
141 ctx = memcache.AddRawFilters(ctx, func(_ context.Context , mc memcache.RawInterface) memcache.RawInterface {
142 gbf.RawInterface = mc
143 return gbf
144 })
145 mc = memcache.Get(ctx)
146 err := TryWithLock(ctx, "testkey", "id", func(ctx contex t.Context) error {
147 // simulate waiting for 64*delay time, and ensur ing that checkLoop
148 // runs that many times.
149 for i := 0; i < 64; i++ {
150 <-blocker
151 clk.Add(delay)
152 }
153 gbf.Lock()
154 mc.Set(mc.NewItem(key).SetValue([]byte("wat")))
155 gbf.Unlock()
156 waitFalse(ctx)
157 return nil
158 })
159 So(err, ShouldBeNil)
160 })
161
162 Convey("can lose it when it gets preemptively released", func() {
163 gbf := &getBlockerFilter{}
164 ctx = memcache.AddRawFilters(ctx, func(_ context.Context , mc memcache.RawInterface) memcache.RawInterface {
165 gbf.RawInterface = mc
166 return gbf
167 })
168 ctx = context.WithValue(ctx, testStopCBKey, func() {
169 gbf.dropAll = true
170 })
171 mc = memcache.Get(ctx)
172 err := TryWithLock(ctx, "testkey", "id", func(ctx contex t.Context) error {
173 // simulate waiting for 64*delay time, and ensur ing that checkLoop
174 // runs that many times.
175 for i := 0; i < 64; i++ {
176 <-blocker
177 clk.Add(delay)
178 }
179 return nil
180 })
181 So(err, ShouldBeNil)
182 })
183
184 Convey("an empty context id is an error", func() {
185 So(TryWithLock(ctx, "testkey", "", nil), ShouldEqual, Er rEmptyClientID)
186 })
187 })
188 }
OLDNEW
« no previous file with comments | « appengine/memlock/memlock.go ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698