github.com/macb/etcd@v0.3.1-0.20140227003422-a60481c6b1a0/store/node.go (about) 1 package store 2 3 import ( 4 "path" 5 "sort" 6 "time" 7 8 etcdErr "github.com/coreos/etcd/error" 9 ustrings "github.com/coreos/etcd/pkg/strings" 10 ) 11 12 var Permanent time.Time 13 14 // node is the basic element in the store system. 15 // A key-value pair will have a string value 16 // A directory will have a children map 17 type node struct { 18 Path string 19 20 CreatedIndex uint64 21 ModifiedIndex uint64 22 23 Parent *node `json:"-"` // should not encode this field! avoid circular dependency. 24 25 ExpireTime time.Time 26 ACL string 27 Value string // for key-value pair 28 Children map[string]*node // for directory 29 30 // A reference to the store this node is attached to. 31 store *store 32 } 33 34 // newKV creates a Key-Value pair 35 func newKV(store *store, nodePath string, value string, createdIndex uint64, 36 parent *node, ACL string, expireTime time.Time) *node { 37 38 return &node{ 39 Path: nodePath, 40 CreatedIndex: createdIndex, 41 ModifiedIndex: createdIndex, 42 Parent: parent, 43 ACL: ACL, 44 store: store, 45 ExpireTime: expireTime, 46 Value: value, 47 } 48 } 49 50 // newDir creates a directory 51 func newDir(store *store, nodePath string, createdIndex uint64, parent *node, 52 ACL string, expireTime time.Time) *node { 53 54 return &node{ 55 Path: nodePath, 56 CreatedIndex: createdIndex, 57 ModifiedIndex: createdIndex, 58 Parent: parent, 59 ACL: ACL, 60 ExpireTime: expireTime, 61 Children: make(map[string]*node), 62 store: store, 63 } 64 } 65 66 // IsHidden function checks if the node is a hidden node. A hidden node 67 // will begin with '_' 68 // A hidden node will not be shown via get command under a directory 69 // For example if we have /foo/_hidden and /foo/notHidden, get "/foo" 70 // will only return /foo/notHidden 71 func (n *node) IsHidden() bool { 72 _, name := path.Split(n.Path) 73 74 return name[0] == '_' 75 } 76 77 // IsPermanent function checks if the node is a permanent one. 78 func (n *node) IsPermanent() bool { 79 // we use a uninitialized time.Time to indicate the node is a 80 // permanent one. 81 // the uninitialized time.Time should equal zero. 82 return n.ExpireTime.IsZero() 83 } 84 85 // IsDir function checks whether the node is a directory. 86 // If the node is a directory, the function will return true. 87 // Otherwise the function will return false. 88 func (n *node) IsDir() bool { 89 return !(n.Children == nil) 90 } 91 92 // Read function gets the value of the node. 93 // If the receiver node is not a key-value pair, a "Not A File" error will be returned. 94 func (n *node) Read() (string, *etcdErr.Error) { 95 if n.IsDir() { 96 return "", etcdErr.NewError(etcdErr.EcodeNotFile, "", n.store.Index()) 97 } 98 99 return n.Value, nil 100 } 101 102 // Write function set the value of the node to the given value. 103 // If the receiver node is a directory, a "Not A File" error will be returned. 104 func (n *node) Write(value string, index uint64) *etcdErr.Error { 105 if n.IsDir() { 106 return etcdErr.NewError(etcdErr.EcodeNotFile, "", n.store.Index()) 107 } 108 109 n.Value = value 110 n.ModifiedIndex = index 111 112 return nil 113 } 114 115 func (n *node) ExpirationAndTTL() (*time.Time, int64) { 116 if !n.IsPermanent() { 117 /* compute ttl as: 118 ceiling( (expireTime - timeNow) / nanosecondsPerSecond ) 119 which ranges from 1..n 120 rather than as: 121 ( (expireTime - timeNow) / nanosecondsPerSecond ) + 1 122 which ranges 1..n+1 123 */ 124 ttlN := n.ExpireTime.Sub(time.Now()) 125 ttl := ttlN / time.Second 126 if (ttlN % time.Second) > 0 { 127 ttl++ 128 } 129 return &n.ExpireTime, int64(ttl) 130 } 131 return nil, 0 132 } 133 134 // List function return a slice of nodes under the receiver node. 135 // If the receiver node is not a directory, a "Not A Directory" error will be returned. 136 func (n *node) List() ([]*node, *etcdErr.Error) { 137 if !n.IsDir() { 138 return nil, etcdErr.NewError(etcdErr.EcodeNotDir, "", n.store.Index()) 139 } 140 141 nodes := make([]*node, len(n.Children)) 142 143 i := 0 144 for _, node := range n.Children { 145 nodes[i] = node 146 i++ 147 } 148 149 return nodes, nil 150 } 151 152 // GetChild function returns the child node under the directory node. 153 // On success, it returns the file node 154 func (n *node) GetChild(name string) (*node, *etcdErr.Error) { 155 if !n.IsDir() { 156 return nil, etcdErr.NewError(etcdErr.EcodeNotDir, n.Path, n.store.Index()) 157 } 158 159 child, ok := n.Children[name] 160 161 if ok { 162 return child, nil 163 } 164 165 return nil, nil 166 } 167 168 // Add function adds a node to the receiver node. 169 // If the receiver is not a directory, a "Not A Directory" error will be returned. 170 // If there is a existing node with the same name under the directory, a "Already Exist" 171 // error will be returned 172 func (n *node) Add(child *node) *etcdErr.Error { 173 if !n.IsDir() { 174 return etcdErr.NewError(etcdErr.EcodeNotDir, "", n.store.Index()) 175 } 176 177 _, name := path.Split(child.Path) 178 179 _, ok := n.Children[name] 180 181 if ok { 182 return etcdErr.NewError(etcdErr.EcodeNodeExist, "", n.store.Index()) 183 } 184 185 n.Children[name] = child 186 187 return nil 188 } 189 190 // Remove function remove the node. 191 func (n *node) Remove(dir, recursive bool, callback func(path string)) *etcdErr.Error { 192 193 if n.IsDir() { 194 if !dir { 195 // cannot delete a directory without recursive set to true 196 return etcdErr.NewError(etcdErr.EcodeNotFile, n.Path, n.store.Index()) 197 } 198 199 if len(n.Children) != 0 && !recursive { 200 // cannot delete a directory if it is not empty and the operation 201 // is not recursive 202 return etcdErr.NewError(etcdErr.EcodeDirNotEmpty, n.Path, n.store.Index()) 203 } 204 } 205 206 if !n.IsDir() { // key-value pair 207 _, name := path.Split(n.Path) 208 209 // find its parent and remove the node from the map 210 if n.Parent != nil && n.Parent.Children[name] == n { 211 delete(n.Parent.Children, name) 212 } 213 214 if callback != nil { 215 callback(n.Path) 216 } 217 218 if !n.IsPermanent() { 219 n.store.ttlKeyHeap.remove(n) 220 } 221 222 return nil 223 } 224 225 for _, child := range n.Children { // delete all children 226 child.Remove(true, true, callback) 227 } 228 229 // delete self 230 _, name := path.Split(n.Path) 231 if n.Parent != nil && n.Parent.Children[name] == n { 232 delete(n.Parent.Children, name) 233 234 if callback != nil { 235 callback(n.Path) 236 } 237 238 if !n.IsPermanent() { 239 n.store.ttlKeyHeap.remove(n) 240 } 241 242 } 243 244 return nil 245 } 246 247 func (n *node) Repr(recurisive, sorted bool) *NodeExtern { 248 if n.IsDir() { 249 node := &NodeExtern{ 250 Key: n.Path, 251 Dir: true, 252 ModifiedIndex: n.ModifiedIndex, 253 CreatedIndex: n.CreatedIndex, 254 } 255 node.Expiration, node.TTL = n.ExpirationAndTTL() 256 257 if !recurisive { 258 return node 259 } 260 261 children, _ := n.List() 262 node.Nodes = make(NodeExterns, len(children)) 263 264 // we do not use the index in the children slice directly 265 // we need to skip the hidden one 266 i := 0 267 268 for _, child := range children { 269 270 if child.IsHidden() { // get will not list hidden node 271 continue 272 } 273 274 node.Nodes[i] = child.Repr(recurisive, sorted) 275 276 i++ 277 } 278 279 // eliminate hidden nodes 280 node.Nodes = node.Nodes[:i] 281 if sorted { 282 sort.Sort(node.Nodes) 283 } 284 285 return node 286 } 287 288 // since n.Value could be changed later, so we need to copy the value out 289 value := ustrings.Clone(n.Value) 290 node := &NodeExtern{ 291 Key: n.Path, 292 Value: &value, 293 ModifiedIndex: n.ModifiedIndex, 294 CreatedIndex: n.CreatedIndex, 295 } 296 node.Expiration, node.TTL = n.ExpirationAndTTL() 297 return node 298 } 299 300 func (n *node) UpdateTTL(expireTime time.Time) { 301 302 if !n.IsPermanent() { 303 if expireTime.IsZero() { 304 // from ttl to permanent 305 // remove from ttl heap 306 n.store.ttlKeyHeap.remove(n) 307 } else { 308 // update ttl 309 n.ExpireTime = expireTime 310 // update ttl heap 311 n.store.ttlKeyHeap.update(n) 312 } 313 314 } else { 315 if !expireTime.IsZero() { 316 // from permanent to ttl 317 n.ExpireTime = expireTime 318 // push into ttl heap 319 n.store.ttlKeyHeap.push(n) 320 } 321 } 322 } 323 324 func (n *node) Compare(prevValue string, prevIndex uint64) bool { 325 compareValue := (prevValue == "" || n.Value == prevValue) 326 compareIndex := (prevIndex == 0 || n.ModifiedIndex == prevIndex) 327 328 return compareValue && compareIndex 329 } 330 331 // Clone function clone the node recursively and return the new node. 332 // If the node is a directory, it will clone all the content under this directory. 333 // If the node is a key-value pair, it will clone the pair. 334 func (n *node) Clone() *node { 335 if !n.IsDir() { 336 return newKV(n.store, n.Path, n.Value, n.CreatedIndex, n.Parent, n.ACL, n.ExpireTime) 337 } 338 339 clone := newDir(n.store, n.Path, n.CreatedIndex, n.Parent, n.ACL, n.ExpireTime) 340 341 for key, child := range n.Children { 342 clone.Children[key] = child.Clone() 343 } 344 345 return clone 346 } 347 348 // recoverAndclean function help to do recovery. 349 // Two things need to be done: 1. recovery structure; 2. delete expired nodes 350 351 // If the node is a directory, it will help recover children's parent pointer and recursively 352 // call this function on its children. 353 // We check the expire last since we need to recover the whole structure first and add all the 354 // notifications into the event history. 355 func (n *node) recoverAndclean() { 356 if n.IsDir() { 357 for _, child := range n.Children { 358 child.Parent = n 359 child.store = n.store 360 child.recoverAndclean() 361 } 362 } 363 364 if !n.ExpireTime.IsZero() { 365 n.store.ttlKeyHeap.push(n) 366 } 367 368 }