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