1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package v2store
16
17 import (
18 "testing"
19 "time"
20
21 "go.etcd.io/etcd/client/pkg/v3/testutil"
22 "go.etcd.io/etcd/server/v3/etcdserver/api/v2error"
23
24 "github.com/jonboulle/clockwork"
25 )
26
27
28 func TestMinExpireTime(t *testing.T) {
29 s := newStore()
30 fc := clockwork.NewFakeClock()
31 s.clock = fc
32
33 testutil.AssertTrue(t, minExpireTime.After(fc.Now()), "minExpireTime should be ahead of FakeClock!")
34 s.Create("/foo", false, "Y", false, TTLOptionSet{ExpireTime: fc.Now().Add(3 * time.Second)})
35 fc.Advance(5 * time.Second)
36
37 s.DeleteExpiredKeys(fc.Now())
38 var eidx uint64 = 1
39 e, err := s.Get("/foo", true, false)
40 testutil.AssertNil(t, err)
41 testutil.AssertEqual(t, e.EtcdIndex, eidx)
42 testutil.AssertEqual(t, e.Action, "get")
43 testutil.AssertEqual(t, e.Node.Key, "/foo")
44 testutil.AssertEqual(t, e.Node.TTL, int64(0))
45 }
46
47
48
49 func TestStoreGetDirectory(t *testing.T) {
50 s := newStore()
51 fc := newFakeClock()
52 s.clock = fc
53 s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
54 s.Create("/foo/bar", false, "X", false, TTLOptionSet{ExpireTime: Permanent})
55 s.Create("/foo/_hidden", false, "*", false, TTLOptionSet{ExpireTime: Permanent})
56 s.Create("/foo/baz", true, "", false, TTLOptionSet{ExpireTime: Permanent})
57 s.Create("/foo/baz/bat", false, "Y", false, TTLOptionSet{ExpireTime: Permanent})
58 s.Create("/foo/baz/_hidden", false, "*", false, TTLOptionSet{ExpireTime: Permanent})
59 s.Create("/foo/baz/ttl", false, "Y", false, TTLOptionSet{ExpireTime: fc.Now().Add(time.Second * 3)})
60 var eidx uint64 = 7
61 e, err := s.Get("/foo", true, false)
62 testutil.AssertNil(t, err)
63 testutil.AssertEqual(t, e.EtcdIndex, eidx)
64 testutil.AssertEqual(t, e.Action, "get")
65 testutil.AssertEqual(t, e.Node.Key, "/foo")
66 testutil.AssertEqual(t, len(e.Node.Nodes), 2)
67 var bazNodes NodeExterns
68 for _, node := range e.Node.Nodes {
69 switch node.Key {
70 case "/foo/bar":
71 testutil.AssertEqual(t, *node.Value, "X")
72 testutil.AssertEqual(t, node.Dir, false)
73 case "/foo/baz":
74 testutil.AssertEqual(t, node.Dir, true)
75 testutil.AssertEqual(t, len(node.Nodes), 2)
76 bazNodes = node.Nodes
77 default:
78 t.Errorf("key = %s, not matched", node.Key)
79 }
80 }
81 for _, node := range bazNodes {
82 switch node.Key {
83 case "/foo/baz/bat":
84 testutil.AssertEqual(t, *node.Value, "Y")
85 testutil.AssertEqual(t, node.Dir, false)
86 case "/foo/baz/ttl":
87 testutil.AssertEqual(t, *node.Value, "Y")
88 testutil.AssertEqual(t, node.Dir, false)
89 testutil.AssertEqual(t, node.TTL, int64(3))
90 default:
91 t.Errorf("key = %s, not matched", node.Key)
92 }
93 }
94 }
95
96
97 func TestStoreUpdateValueTTL(t *testing.T) {
98 s := newStore()
99 fc := newFakeClock()
100 s.clock = fc
101
102 var eidx uint64 = 2
103 s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
104 _, err := s.Update("/foo", "baz", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
105 testutil.AssertNil(t, err)
106 e, _ := s.Get("/foo", false, false)
107 testutil.AssertEqual(t, *e.Node.Value, "baz")
108 testutil.AssertEqual(t, e.EtcdIndex, eidx)
109 fc.Advance(600 * time.Millisecond)
110 s.DeleteExpiredKeys(fc.Now())
111 e, err = s.Get("/foo", false, false)
112 testutil.AssertNil(t, e)
113 testutil.AssertEqual(t, err.(*v2error.Error).ErrorCode, v2error.EcodeKeyNotFound)
114 }
115
116
117 func TestStoreUpdateDirTTL(t *testing.T) {
118 s := newStore()
119 fc := newFakeClock()
120 s.clock = fc
121
122 var eidx uint64 = 3
123 _, err := s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
124 testutil.AssertNil(t, err)
125 _, err = s.Create("/foo/bar", false, "baz", false, TTLOptionSet{ExpireTime: Permanent})
126 testutil.AssertNil(t, err)
127 e, err := s.Update("/foo/bar", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
128 testutil.AssertNil(t, err)
129 testutil.AssertEqual(t, e.Node.Dir, false)
130 testutil.AssertEqual(t, e.EtcdIndex, eidx)
131 e, _ = s.Get("/foo/bar", false, false)
132 testutil.AssertEqual(t, *e.Node.Value, "")
133 testutil.AssertEqual(t, e.EtcdIndex, eidx)
134
135 fc.Advance(600 * time.Millisecond)
136 s.DeleteExpiredKeys(fc.Now())
137 e, err = s.Get("/foo/bar", false, false)
138 testutil.AssertNil(t, e)
139 testutil.AssertEqual(t, err.(*v2error.Error).ErrorCode, v2error.EcodeKeyNotFound)
140 }
141
142
143 func TestStoreWatchExpire(t *testing.T) {
144 s := newStore()
145 fc := newFakeClock()
146 s.clock = fc
147
148 var eidx uint64 = 3
149 s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(400 * time.Millisecond)})
150 s.Create("/foofoo", false, "barbarbar", false, TTLOptionSet{ExpireTime: fc.Now().Add(450 * time.Millisecond)})
151 s.Create("/foodir", true, "", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
152
153 w, _ := s.Watch("/", true, false, 0)
154 testutil.AssertEqual(t, w.StartIndex(), eidx)
155 c := w.EventChan()
156 e := nbselect(c)
157 testutil.AssertNil(t, e)
158 fc.Advance(600 * time.Millisecond)
159 s.DeleteExpiredKeys(fc.Now())
160 eidx = 4
161 e = nbselect(c)
162 testutil.AssertEqual(t, e.EtcdIndex, eidx)
163 testutil.AssertEqual(t, e.Action, "expire")
164 testutil.AssertEqual(t, e.Node.Key, "/foo")
165 w, _ = s.Watch("/", true, false, 5)
166 eidx = 6
167 testutil.AssertEqual(t, w.StartIndex(), eidx)
168 e = nbselect(w.EventChan())
169 testutil.AssertEqual(t, e.EtcdIndex, eidx)
170 testutil.AssertEqual(t, e.Action, "expire")
171 testutil.AssertEqual(t, e.Node.Key, "/foofoo")
172 w, _ = s.Watch("/", true, false, 6)
173 e = nbselect(w.EventChan())
174 testutil.AssertEqual(t, e.EtcdIndex, eidx)
175 testutil.AssertEqual(t, e.Action, "expire")
176 testutil.AssertEqual(t, e.Node.Key, "/foodir")
177 testutil.AssertEqual(t, e.Node.Dir, true)
178 }
179
180
181 func TestStoreWatchExpireRefresh(t *testing.T) {
182 s := newStore()
183 fc := newFakeClock()
184 s.clock = fc
185
186 var eidx uint64 = 2
187 s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
188 s.Create("/foofoo", false, "barbarbar", false, TTLOptionSet{ExpireTime: fc.Now().Add(1200 * time.Millisecond), Refresh: true})
189
190
191 w, _ := s.Watch("/", true, false, 0)
192 testutil.AssertEqual(t, w.StartIndex(), eidx)
193 c := w.EventChan()
194 e := nbselect(c)
195 testutil.AssertNil(t, e)
196 fc.Advance(600 * time.Millisecond)
197 s.DeleteExpiredKeys(fc.Now())
198 eidx = 3
199 e = nbselect(c)
200 testutil.AssertEqual(t, e.EtcdIndex, eidx)
201 testutil.AssertEqual(t, e.Action, "expire")
202 testutil.AssertEqual(t, e.Node.Key, "/foo")
203
204 s.Update("/foofoo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
205 w, _ = s.Watch("/", true, false, 4)
206 fc.Advance(700 * time.Millisecond)
207 s.DeleteExpiredKeys(fc.Now())
208 eidx = 5
209 testutil.AssertEqual(t, w.StartIndex(), eidx-1)
210 e = nbselect(w.EventChan())
211 testutil.AssertEqual(t, e.EtcdIndex, eidx)
212 testutil.AssertEqual(t, e.Action, "expire")
213 testutil.AssertEqual(t, e.Node.Key, "/foofoo")
214 }
215
216
217 func TestStoreWatchExpireEmptyRefresh(t *testing.T) {
218 s := newStore()
219 fc := newFakeClock()
220 s.clock = fc
221
222 var eidx uint64
223 s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
224
225 fc.Advance(200 * time.Millisecond)
226 s.DeleteExpiredKeys(fc.Now())
227
228 s.Update("/foo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
229 w, _ := s.Watch("/", true, false, 2)
230 fc.Advance(700 * time.Millisecond)
231 s.DeleteExpiredKeys(fc.Now())
232 eidx = 3
233 testutil.AssertEqual(t, w.StartIndex(), eidx-1)
234 e := nbselect(w.EventChan())
235 testutil.AssertEqual(t, e.EtcdIndex, eidx)
236 testutil.AssertEqual(t, e.Action, "expire")
237 testutil.AssertEqual(t, e.Node.Key, "/foo")
238 testutil.AssertEqual(t, *e.PrevNode.Value, "bar")
239 }
240
241
242 func TestStoreWatchNoRefresh(t *testing.T) {
243 s := newStore()
244 fc := newFakeClock()
245 s.clock = fc
246
247 var eidx uint64
248 s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
249
250 fc.Advance(200 * time.Millisecond)
251 s.DeleteExpiredKeys(fc.Now())
252
253
254 s.Update("/foo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: false})
255 w, _ := s.Watch("/", true, false, 2)
256 fc.Advance(700 * time.Millisecond)
257 s.DeleteExpiredKeys(fc.Now())
258 eidx = 2
259 testutil.AssertEqual(t, w.StartIndex(), eidx)
260 e := nbselect(w.EventChan())
261 testutil.AssertEqual(t, e.EtcdIndex, eidx)
262 testutil.AssertEqual(t, e.Action, "update")
263 testutil.AssertEqual(t, e.Node.Key, "/foo")
264 testutil.AssertEqual(t, *e.PrevNode.Value, "bar")
265 }
266
267
268 func TestStoreRefresh(t *testing.T) {
269 s := newStore()
270 fc := newFakeClock()
271 s.clock = fc
272
273 s.Create("/foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
274 s.Create("/bar", true, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
275 s.Create("/bar/z", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
276 _, err := s.Update("/foo", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
277 testutil.AssertNil(t, err)
278
279 _, err = s.Set("/foo", false, "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
280 testutil.AssertNil(t, err)
281
282 _, err = s.Update("/bar/z", "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
283 testutil.AssertNil(t, err)
284
285 _, err = s.CompareAndSwap("/foo", "bar", 0, "", TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond), Refresh: true})
286 testutil.AssertNil(t, err)
287 }
288
289
290 func TestStoreRecoverWithExpiration(t *testing.T) {
291 s := newStore()
292 s.clock = newFakeClock()
293
294 fc := newFakeClock()
295
296 var eidx uint64 = 4
297 s.Create("/foo", true, "", false, TTLOptionSet{ExpireTime: Permanent})
298 s.Create("/foo/x", false, "bar", false, TTLOptionSet{ExpireTime: Permanent})
299 s.Create("/foo/y", false, "baz", false, TTLOptionSet{ExpireTime: fc.Now().Add(5 * time.Millisecond)})
300 b, err := s.Save()
301 testutil.AssertNil(t, err)
302
303 time.Sleep(10 * time.Millisecond)
304
305 s2 := newStore()
306 s2.clock = fc
307
308 s2.Recovery(b)
309
310 fc.Advance(600 * time.Millisecond)
311 s.DeleteExpiredKeys(fc.Now())
312
313 e, err := s.Get("/foo/x", false, false)
314 testutil.AssertNil(t, err)
315 testutil.AssertEqual(t, e.EtcdIndex, eidx)
316 testutil.AssertEqual(t, *e.Node.Value, "bar")
317
318 e, err = s.Get("/foo/y", false, false)
319 testutil.AssertNotNil(t, err)
320 testutil.AssertNil(t, e)
321 }
322
323
324 func TestStoreWatchExpireWithHiddenKey(t *testing.T) {
325 s := newStore()
326 fc := newFakeClock()
327 s.clock = fc
328
329 s.Create("/_foo", false, "bar", false, TTLOptionSet{ExpireTime: fc.Now().Add(500 * time.Millisecond)})
330 s.Create("/foofoo", false, "barbarbar", false, TTLOptionSet{ExpireTime: fc.Now().Add(1000 * time.Millisecond)})
331
332 w, _ := s.Watch("/", true, false, 0)
333 c := w.EventChan()
334 e := nbselect(c)
335 testutil.AssertNil(t, e)
336 fc.Advance(600 * time.Millisecond)
337 s.DeleteExpiredKeys(fc.Now())
338 e = nbselect(c)
339 testutil.AssertNil(t, e)
340 fc.Advance(600 * time.Millisecond)
341 s.DeleteExpiredKeys(fc.Now())
342 e = nbselect(c)
343 testutil.AssertEqual(t, e.Action, "expire")
344 testutil.AssertEqual(t, e.Node.Key, "/foofoo")
345 }
346
347
348 func newFakeClock() clockwork.FakeClock {
349 fc := clockwork.NewFakeClock()
350 for minExpireTime.After(fc.Now()) {
351 fc.Advance((0x1 << 62) * time.Nanosecond)
352 }
353 return fc
354 }
355
356
357 func nbselect(c <-chan *Event) *Event {
358 select {
359 case e := <-c:
360 return e
361 default:
362 return nil
363 }
364 }
365
View as plain text