go.etcd.io/etcd@v3.3.27+incompatible/store/node.go (about)

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