1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package v2store
16
17 import (
18 "path"
19 "sort"
20 "time"
21
22 "go.etcd.io/etcd/server/v3/etcdserver/api/v2error"
23
24 "github.com/jonboulle/clockwork"
25 )
26
27
28 const (
29 CompareMatch = iota
30 CompareIndexNotMatch
31 CompareValueNotMatch
32 CompareNotMatch
33 )
34
35 var Permanent time.Time
36
37
38
39
40 type node struct {
41 Path string
42
43 CreatedIndex uint64
44 ModifiedIndex uint64
45
46 Parent *node `json:"-"`
47
48 ExpireTime time.Time
49 Value string
50 Children map[string]*node
51
52
53 store *store
54 }
55
56
57 func newKV(store *store, nodePath string, value string, createdIndex uint64, parent *node, expireTime time.Time) *node {
58 return &node{
59 Path: nodePath,
60 CreatedIndex: createdIndex,
61 ModifiedIndex: createdIndex,
62 Parent: parent,
63 store: store,
64 ExpireTime: expireTime,
65 Value: value,
66 }
67 }
68
69
70 func newDir(store *store, nodePath string, createdIndex uint64, parent *node, expireTime time.Time) *node {
71 return &node{
72 Path: nodePath,
73 CreatedIndex: createdIndex,
74 ModifiedIndex: createdIndex,
75 Parent: parent,
76 ExpireTime: expireTime,
77 Children: make(map[string]*node),
78 store: store,
79 }
80 }
81
82
83
84
85
86
87 func (n *node) IsHidden() bool {
88 _, name := path.Split(n.Path)
89
90 return name[0] == '_'
91 }
92
93
94 func (n *node) IsPermanent() bool {
95
96
97
98 return n.ExpireTime.IsZero()
99 }
100
101
102
103
104 func (n *node) IsDir() bool {
105 return n.Children != nil
106 }
107
108
109
110 func (n *node) Read() (string, *v2error.Error) {
111 if n.IsDir() {
112 return "", v2error.NewError(v2error.EcodeNotFile, "", n.store.CurrentIndex)
113 }
114
115 return n.Value, nil
116 }
117
118
119
120 func (n *node) Write(value string, index uint64) *v2error.Error {
121 if n.IsDir() {
122 return v2error.NewError(v2error.EcodeNotFile, "", n.store.CurrentIndex)
123 }
124
125 n.Value = value
126 n.ModifiedIndex = index
127
128 return nil
129 }
130
131 func (n *node) expirationAndTTL(clock clockwork.Clock) (*time.Time, int64) {
132 if !n.IsPermanent() {
133
140 ttlN := n.ExpireTime.Sub(clock.Now())
141 ttl := ttlN / time.Second
142 if (ttlN % time.Second) > 0 {
143 ttl++
144 }
145 t := n.ExpireTime.UTC()
146 return &t, int64(ttl)
147 }
148 return nil, 0
149 }
150
151
152
153 func (n *node) List() ([]*node, *v2error.Error) {
154 if !n.IsDir() {
155 return nil, v2error.NewError(v2error.EcodeNotDir, "", n.store.CurrentIndex)
156 }
157
158 nodes := make([]*node, len(n.Children))
159
160 i := 0
161 for _, node := range n.Children {
162 nodes[i] = node
163 i++
164 }
165
166 return nodes, nil
167 }
168
169
170
171 func (n *node) GetChild(name string) (*node, *v2error.Error) {
172 if !n.IsDir() {
173 return nil, v2error.NewError(v2error.EcodeNotDir, n.Path, n.store.CurrentIndex)
174 }
175
176 child, ok := n.Children[name]
177
178 if ok {
179 return child, nil
180 }
181
182 return nil, nil
183 }
184
185
186
187
188
189 func (n *node) Add(child *node) *v2error.Error {
190 if !n.IsDir() {
191 return v2error.NewError(v2error.EcodeNotDir, "", n.store.CurrentIndex)
192 }
193
194 _, name := path.Split(child.Path)
195
196 if _, ok := n.Children[name]; ok {
197 return v2error.NewError(v2error.EcodeNodeExist, "", n.store.CurrentIndex)
198 }
199
200 n.Children[name] = child
201
202 return nil
203 }
204
205
206 func (n *node) Remove(dir, recursive bool, callback func(path string)) *v2error.Error {
207 if !n.IsDir() {
208 _, name := path.Split(n.Path)
209
210
211 if n.Parent != nil && n.Parent.Children[name] == n {
212 delete(n.Parent.Children, name)
213 }
214
215 if callback != nil {
216 callback(n.Path)
217 }
218
219 if !n.IsPermanent() {
220 n.store.ttlKeyHeap.remove(n)
221 }
222
223 return nil
224 }
225
226 if !dir {
227
228 return v2error.NewError(v2error.EcodeNotFile, n.Path, n.store.CurrentIndex)
229 }
230
231 if len(n.Children) != 0 && !recursive {
232
233
234 return v2error.NewError(v2error.EcodeDirNotEmpty, n.Path, n.store.CurrentIndex)
235 }
236
237 for _, child := range n.Children {
238 child.Remove(true, true, callback)
239 }
240
241
242 _, name := path.Split(n.Path)
243 if n.Parent != nil && n.Parent.Children[name] == n {
244 delete(n.Parent.Children, name)
245
246 if callback != nil {
247 callback(n.Path)
248 }
249
250 if !n.IsPermanent() {
251 n.store.ttlKeyHeap.remove(n)
252 }
253 }
254
255 return nil
256 }
257
258 func (n *node) Repr(recursive, sorted bool, clock clockwork.Clock) *NodeExtern {
259 if n.IsDir() {
260 node := &NodeExtern{
261 Key: n.Path,
262 Dir: true,
263 ModifiedIndex: n.ModifiedIndex,
264 CreatedIndex: n.CreatedIndex,
265 }
266 node.Expiration, node.TTL = n.expirationAndTTL(clock)
267
268 if !recursive {
269 return node
270 }
271
272 children, _ := n.List()
273 node.Nodes = make(NodeExterns, len(children))
274
275
276
277 i := 0
278
279 for _, child := range children {
280
281 if child.IsHidden() {
282 continue
283 }
284
285 node.Nodes[i] = child.Repr(recursive, sorted, clock)
286
287 i++
288 }
289
290
291 node.Nodes = node.Nodes[:i]
292 if sorted {
293 sort.Sort(node.Nodes)
294 }
295
296 return node
297 }
298
299
300 value := n.Value
301 node := &NodeExtern{
302 Key: n.Path,
303 Value: &value,
304 ModifiedIndex: n.ModifiedIndex,
305 CreatedIndex: n.CreatedIndex,
306 }
307 node.Expiration, node.TTL = n.expirationAndTTL(clock)
308 return node
309 }
310
311 func (n *node) UpdateTTL(expireTime time.Time) {
312 if !n.IsPermanent() {
313 if expireTime.IsZero() {
314
315 n.ExpireTime = expireTime
316
317 n.store.ttlKeyHeap.remove(n)
318 return
319 }
320
321
322 n.ExpireTime = expireTime
323
324 n.store.ttlKeyHeap.update(n)
325 return
326 }
327
328 if expireTime.IsZero() {
329 return
330 }
331
332
333 n.ExpireTime = expireTime
334
335 n.store.ttlKeyHeap.push(n)
336 }
337
338
339
340 func (n *node) Compare(prevValue string, prevIndex uint64) (ok bool, which int) {
341 indexMatch := prevIndex == 0 || n.ModifiedIndex == prevIndex
342 valueMatch := prevValue == "" || n.Value == prevValue
343 ok = valueMatch && indexMatch
344 switch {
345 case valueMatch && indexMatch:
346 which = CompareMatch
347 case indexMatch && !valueMatch:
348 which = CompareValueNotMatch
349 case valueMatch && !indexMatch:
350 which = CompareIndexNotMatch
351 default:
352 which = CompareNotMatch
353 }
354 return ok, which
355 }
356
357
358
359
360 func (n *node) Clone() *node {
361 if !n.IsDir() {
362 newkv := newKV(n.store, n.Path, n.Value, n.CreatedIndex, n.Parent, n.ExpireTime)
363 newkv.ModifiedIndex = n.ModifiedIndex
364 return newkv
365 }
366
367 clone := newDir(n.store, n.Path, n.CreatedIndex, n.Parent, n.ExpireTime)
368 clone.ModifiedIndex = n.ModifiedIndex
369
370 for key, child := range n.Children {
371 clone.Children[key] = child.Clone()
372 }
373
374 return clone
375 }
376
377
378
379
380
381
382
383
384 func (n *node) recoverAndclean() {
385 if n.IsDir() {
386 for _, child := range n.Children {
387 child.Parent = n
388 child.store = n.store
389 child.recoverAndclean()
390 }
391 }
392
393 if !n.ExpireTime.IsZero() {
394 n.store.ttlKeyHeap.push(n)
395 }
396 }
397
View as plain text