node.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. package store
  2. import (
  3. "path"
  4. "sort"
  5. "sync"
  6. "time"
  7. etcdErr "github.com/coreos/etcd/error"
  8. )
  9. const (
  10. normal = iota
  11. removed
  12. )
  13. var Permanent time.Time
  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. CreateIndex uint64
  20. CreateTerm uint64
  21. ModifiedIndex uint64
  22. ModifiedTerm uint64
  23. Parent *Node `json:"-"` // should not encode this field! avoid cyclical dependency.
  24. ExpireTime time.Time
  25. ACL string
  26. Value string // for key-value pair
  27. Children map[string]*Node // for directory
  28. // A reference to the store this node is attached to.
  29. store *store
  30. // ensure we only delete the node once
  31. // expire and remove may try to delete a node twice
  32. once sync.Once
  33. }
  34. // newKV creates a Key-Value pair
  35. func newKV(store *store, nodePath string, value string, createIndex uint64,
  36. createTerm uint64, parent *Node, ACL string, expireTime time.Time) *Node {
  37. return &Node{
  38. Path: nodePath,
  39. CreateIndex: createIndex,
  40. CreateTerm: createTerm,
  41. ModifiedIndex: createIndex,
  42. ModifiedTerm: createTerm,
  43. Parent: parent,
  44. ACL: ACL,
  45. store: store,
  46. ExpireTime: expireTime,
  47. Value: value,
  48. }
  49. }
  50. // newDir creates a directory
  51. func newDir(store *store, nodePath string, createIndex uint64, createTerm uint64,
  52. parent *Node, ACL string, expireTime time.Time) *Node {
  53. return &Node{
  54. Path: nodePath,
  55. CreateIndex: createIndex,
  56. CreateTerm: createTerm,
  57. Parent: parent,
  58. ACL: ACL,
  59. ExpireTime: expireTime,
  60. Children: make(map[string]*Node),
  61. store: store,
  62. }
  63. }
  64. // IsHidden function checks if the node is a hidden node. A hidden node
  65. // will begin with '_'
  66. // A hidden node will not be shown via get command under a directory
  67. // For example if we have /foo/_hidden and /foo/notHidden, get "/foo"
  68. // will only return /foo/notHidden
  69. func (n *Node) IsHidden() bool {
  70. _, name := path.Split(n.Path)
  71. return name[0] == '_'
  72. }
  73. // IsPermanent function checks if the node is a permanent one.
  74. func (n *Node) IsPermanent() bool {
  75. return n.ExpireTime.IsZero()
  76. }
  77. // IsDir function checks whether the node is a directory.
  78. // If the node is a directory, the function will return true.
  79. // Otherwise the function will return false.
  80. func (n *Node) IsDir() bool {
  81. return !(n.Children == nil)
  82. }
  83. // Read function gets the value of the node.
  84. // If the receiver node is not a key-value pair, a "Not A File" error will be returned.
  85. func (n *Node) Read() (string, *etcdErr.Error) {
  86. if n.IsDir() {
  87. return "", etcdErr.NewError(etcdErr.EcodeNotFile, "", UndefIndex, UndefTerm)
  88. }
  89. return n.Value, nil
  90. }
  91. // Write function set the value of the node to the given value.
  92. // If the receiver node is a directory, a "Not A File" error will be returned.
  93. func (n *Node) Write(value string, index uint64, term uint64) *etcdErr.Error {
  94. if n.IsDir() {
  95. return etcdErr.NewError(etcdErr.EcodeNotFile, "", UndefIndex, UndefTerm)
  96. }
  97. n.Value = value
  98. n.ModifiedIndex = index
  99. n.ModifiedTerm = term
  100. return nil
  101. }
  102. func (n *Node) ExpirationAndTTL() (*time.Time, int64) {
  103. if !n.IsPermanent() {
  104. return &n.ExpireTime, int64(n.ExpireTime.Sub(time.Now())/time.Second) + 1
  105. }
  106. return nil, 0
  107. }
  108. // List function return a slice of nodes under the receiver node.
  109. // If the receiver node is not a directory, a "Not A Directory" error will be returned.
  110. func (n *Node) List() ([]*Node, *etcdErr.Error) {
  111. if !n.IsDir() {
  112. return nil, etcdErr.NewError(etcdErr.EcodeNotDir, "", UndefIndex, UndefTerm)
  113. }
  114. nodes := make([]*Node, len(n.Children))
  115. i := 0
  116. for _, node := range n.Children {
  117. nodes[i] = node
  118. i++
  119. }
  120. return nodes, nil
  121. }
  122. // GetChild function returns the child node under the directory node.
  123. // On success, it returns the file node
  124. func (n *Node) GetChild(name string) (*Node, *etcdErr.Error) {
  125. if !n.IsDir() {
  126. return nil, etcdErr.NewError(etcdErr.EcodeNotDir, n.Path, UndefIndex, UndefTerm)
  127. }
  128. child, ok := n.Children[name]
  129. if ok {
  130. return child, nil
  131. }
  132. return nil, nil
  133. }
  134. // Add function adds a node to the receiver node.
  135. // If the receiver is not a directory, a "Not A Directory" error will be returned.
  136. // If there is a existing node with the same name under the directory, a "Already Exist"
  137. // error will be returned
  138. func (n *Node) Add(child *Node) *etcdErr.Error {
  139. if !n.IsDir() {
  140. return etcdErr.NewError(etcdErr.EcodeNotDir, "", UndefIndex, UndefTerm)
  141. }
  142. _, name := path.Split(child.Path)
  143. _, ok := n.Children[name]
  144. if ok {
  145. return etcdErr.NewError(etcdErr.EcodeNodeExist, "", UndefIndex, UndefTerm)
  146. }
  147. n.Children[name] = child
  148. return nil
  149. }
  150. // Remove function remove the node.
  151. func (n *Node) Remove(recursive bool, callback func(path string)) *etcdErr.Error {
  152. if n.IsDir() && !recursive {
  153. // cannot delete a directory without set recursive to true
  154. return etcdErr.NewError(etcdErr.EcodeNotFile, "", UndefIndex, UndefTerm)
  155. }
  156. if !n.IsDir() { // key-value pair
  157. _, name := path.Split(n.Path)
  158. // find its parent and remove the node from the map
  159. if n.Parent != nil && n.Parent.Children[name] == n {
  160. delete(n.Parent.Children, name)
  161. }
  162. if callback != nil {
  163. callback(n.Path)
  164. }
  165. if !n.IsPermanent() {
  166. n.store.ttlKeyHeap.remove(n)
  167. }
  168. return nil
  169. }
  170. for _, child := range n.Children { // delete all children
  171. child.Remove(true, callback)
  172. }
  173. // delete self
  174. _, name := path.Split(n.Path)
  175. if n.Parent != nil && n.Parent.Children[name] == n {
  176. delete(n.Parent.Children, name)
  177. if callback != nil {
  178. callback(n.Path)
  179. }
  180. if !n.IsPermanent() {
  181. n.store.ttlKeyHeap.remove(n)
  182. }
  183. }
  184. return nil
  185. }
  186. func (n *Node) Pair(recurisive, sorted bool) KeyValuePair {
  187. if n.IsDir() {
  188. pair := KeyValuePair{
  189. Key: n.Path,
  190. Dir: true,
  191. }
  192. pair.Expiration, pair.TTL = n.ExpirationAndTTL()
  193. if !recurisive {
  194. return pair
  195. }
  196. children, _ := n.List()
  197. pair.KVPairs = make([]KeyValuePair, len(children))
  198. // we do not use the index in the children slice directly
  199. // we need to skip the hidden one
  200. i := 0
  201. for _, child := range children {
  202. if child.IsHidden() { // get will not list hidden node
  203. continue
  204. }
  205. pair.KVPairs[i] = child.Pair(recurisive, sorted)
  206. i++
  207. }
  208. // eliminate hidden nodes
  209. pair.KVPairs = pair.KVPairs[:i]
  210. if sorted {
  211. sort.Sort(pair.KVPairs)
  212. }
  213. return pair
  214. }
  215. pair := KeyValuePair{
  216. Key: n.Path,
  217. Value: n.Value,
  218. }
  219. pair.Expiration, pair.TTL = n.ExpirationAndTTL()
  220. return pair
  221. }
  222. func (n *Node) UpdateTTL(expireTime time.Time) {
  223. if !n.IsPermanent() {
  224. if expireTime.IsZero() {
  225. // from ttl to permanent
  226. // remove from ttl heap
  227. n.store.ttlKeyHeap.remove(n)
  228. } else {
  229. // update ttl
  230. // update ttl heap
  231. n.store.ttlKeyHeap.update(n)
  232. }
  233. } else {
  234. if !expireTime.IsZero() {
  235. // from permanent to ttl
  236. // push into ttl heap
  237. n.store.ttlKeyHeap.push(n)
  238. }
  239. }
  240. n.ExpireTime = expireTime
  241. }
  242. // Clone function clone the node recursively and return the new node.
  243. // If the node is a directory, it will clone all the content under this directory.
  244. // If the node is a key-value pair, it will clone the pair.
  245. func (n *Node) Clone() *Node {
  246. if !n.IsDir() {
  247. return newKV(n.store, n.Path, n.Value, n.CreateIndex, n.CreateTerm, n.Parent, n.ACL, n.ExpireTime)
  248. }
  249. clone := newDir(n.store, n.Path, n.CreateIndex, n.CreateTerm, n.Parent, n.ACL, n.ExpireTime)
  250. for key, child := range n.Children {
  251. clone.Children[key] = child.Clone()
  252. }
  253. return clone
  254. }
  255. // recoverAndclean function help to do recovery.
  256. // Two things need to be done: 1. recovery structure; 2. delete expired nodes
  257. // If the node is a directory, it will help recover children's parent pointer and recursively
  258. // call this function on its children.
  259. // We check the expire last since we need to recover the whole structure first and add all the
  260. // notifications into the event history.
  261. func (n *Node) recoverAndclean() {
  262. if n.IsDir() {
  263. for _, child := range n.Children {
  264. child.Parent = n
  265. child.store = n.store
  266. child.recoverAndclean()
  267. }
  268. }
  269. if !n.ExpireTime.IsZero() {
  270. n.store.ttlKeyHeap.push(n)
  271. }
  272. }