github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/iavl/docs/tree/mutable_tree.md (about)

     1  # Mutable Tree
     2  
     3  ### Structure
     4  
     5  The MutableTree struct is a wrapper around ImmutableTree to allow for updates that get stored in successive versions.
     6  
     7  The MutableTree stores the last saved ImmutableTree and the current working tree in its struct while all other saved, available versions are accessible from the nodeDB.
     8  
     9  ```golang
    10  // MutableTree is a persistent tree which keeps track of versions.
    11  type MutableTree struct {
    12  	*ImmutableTree                  // The current, working tree.
    13  	lastSaved      *ImmutableTree   // The most recently saved tree.
    14  	orphans        map[string]int64 // Nodes removed by changes to working tree.
    15  	versions       map[int64]bool   // The previous, saved versions of the tree.
    16  	ndb            *nodeDB
    17  }
    18  ```
    19  
    20  The versions map stores which versions of the IAVL are stored in nodeDB. Anytime a version `v` gets persisted, `versions[v]` is set to `true`. Anytime a version gets deleted, `versions[v]` is set to false.
    21  
    22  ### Set
    23  
    24  Set can be used to add a new key-value pair to the IAVL tree, or to update an existing key with a new value.
    25  
    26  Set starts at the root of the IAVL tree, if the key is less than or equal to the root key, it recursively calls set on root's left child. Else, it recursively calls set on root's right child. It continues to recurse down the IAVL tree based on comparing the set key and the node key until it reaches a leaf node.
    27  
    28  If the leaf node has the same key as the set key, then the set is just updating an existing key with a new value. The value is updated, and the old version of the node is orphaned.
    29  
    30  If the leaf node does not have the same key as the set key, then the set is trying to add a new key to the IAVL tree. The leaf node is replaced by an inner node that has the original leaf node and the new node from the set call as its children.
    31  
    32  If the `setKey` < `leafKey`:
    33  
    34  ```golang
    35  // new leaf node that gets created by Set
    36  // since this is a new update since latest saved version,
    37  // this node has version=latestVersion+1
    38  newVersion := latestVersion+1
    39  newNode := NewNode(key, value, newVersion)
    40  // original leaf node: originalLeaf gets replaced by inner node below
    41  Node{
    42      key:       setKey,       // inner node key is equal to left child's key
    43      height:    1,            // height=1 since node is parent of leaves
    44      size:      2,            // 2 leaf nodes under this node
    45      leftNode:  newNode,      // left Node is the new added leaf node
    46      rightNode: originalLeaf, // right Node is the original leaf node
    47      version:   newVersion,   // inner node is new since lastVersion, so it has an incremented version
    48  }
    49  ```
    50  
    51  If `setKey` > `leafKey`:
    52  
    53  ```golang
    54  // new leaf node that gets created by Set
    55  // since this is a new update since latest saved version,
    56  // this node has version=latestVersion+1
    57  newVersion := latestVersion+1
    58  newNode := NewNode(key, value, latestVersion+1)
    59  // original leaf node: originalLeaf gets replaced by inner node below
    60  Node{
    61      key:       leafKey,      // inner node key is equal to left child's key
    62      height:    1,            // height=1 since node is parent of leaves
    63      size:      2,            // 2 leaf nodes under this node
    64      leftNode:  originalLeaf, // left Node is the original leaf node
    65      rightNode: newNode,      // right Node  is the new added leaf node
    66      version:   newVersion,   // inner node is new since lastVersion, so it has an incremented version
    67  }
    68  ```
    69  
    70  Any node that gets recursed upon during a Set call is necessarily orphaned since it will either have a new value (in the case of an update) or it will have a new descendant. The recursive calls accumulate a list of orphans as it descends down the IAVL tree. This list of orphans is ultimately added to the mutable tree's orphan list at the end of the Set call.
    71  
    72  After each set, the current working tree has its height and size recalculated. If the height of the left branch and right branch of the working tree differs by more than one, then the mutable tree has to be balanced before the Set call can return.
    73  
    74  ### Remove
    75  
    76  Remove is another recursive function to remove a key-value pair from the IAVL pair. If the key that is trying to be removed does not exist, Remove is a no-op.
    77  
    78  Remove recurses down the IAVL tree in the same way that Set does until it reaches a leaf node. If the leaf node's key is equal to the remove key, the node is removed, and all of its parents are recursively updated. If not, the remove call does nothing.
    79  
    80  #### Recursive Remove
    81  
    82  Remove works by calling an inner function `recursiveRemove` that returns the following values after a recursive call `recursiveRemove(recurseNode, removeKey)`:
    83  
    84  ##### NewHash
    85  
    86  If a node in recurseNode's subtree gets removed, then the hash of the recurseNode will change. Thus during the recursive calls down the subtree, all of recurseNode's children will return their new hashes after the remove (if they have changed). Using this information, recurseNode can calculate its own updated hash and return that value.
    87  
    88  If recurseNode is the node getting removed itself, NewHash is `nil`.
    89  
    90  ##### ReplaceNode
    91  
    92  Just like with recursiveSet, any node that gets recursed upon (in a successful remove) will get orphaned since its hash must be updated and the nodes are immutable. Thus, ReplaceNode is the new node that replaces `recurseNode`.
    93  
    94  If recurseNode is the leaf that gets removed, then ReplaceNode is `nil`.
    95  
    96  If recurseNode is the direct parent of the leaf that got removed, then it can simply be replaced by the other child. Since the parent of recurseNode can directly refer to recurseNode's remaining child. For example if recurseNode's left child gets removed, the following happens:
    97  
    98  
    99  Before LeftLeaf removed:
   100  ```
   101                          |---RightLeaf
   102  IAVLTREE---recurseNode--|
   103                          |---LeftLeaf
   104  ```
   105  
   106  After LeftLeaf removed:
   107  ```
   108  IAVLTREE---RightLeaf
   109  
   110  ReplaceNode = RightLeaf
   111  orphaned = [LeftLeaf, recurseNode]
   112  ```
   113  
   114  If recurseNode is an inner node that got called in the recursiveRemove, but is not a direct parent of the removed leaf. Then an updated version of the node will exist in the tree. Notably, it will have an incremented version, a new hash (as explained in the `NewHash` section), and recalculated height and size.
   115  
   116  The ReplaceNode will be a cloned version of `recurseNode` with an incremented version. The hash will be updated given the NewHash of recurseNode's left child or right child (depending on which branch got recurse upon).
   117  
   118  The height and size of the ReplaceNode will have to be calculated since these values can change after the `remove`.
   119  
   120  It's possible that the subtree for `ReplaceNode` will have to be rebalanced (see `Balance` section). If this is the case, this will also update `ReplaceNode`'s hash since the structure of `ReplaceNode`'s subtree will change.
   121  
   122  ##### LeftmostLeafKey
   123  
   124  The LeftmostLeafKey is the key of the leftmost leaf of `recurseNode`'s subtree. This is only used if `recurseNode` is the right child of its parent. Since inner nodes should have their key equal to the leftmost key of their right branch (if leftmostkey is not `nil`). If recurseNode is the right child of its parent `parentNode`, `parentNode` will set its key to `parentNode.key = leftMostKeyOfRecurseNodeSubTree`.
   125  
   126  If `recurseNode` is a leaf, it will return `nil`.
   127  
   128  If `recurseNode` is a parent of the leaf that got removed, it will return its own key if the left child was removed. If the right child is removed, it will return `nil`.
   129  
   130  If `recurseNode` is a generic inner node that isn't a direct parent of the removed node, it will return the leftmost key of its child's recursive call if `node.key < removeKey`. It will return `nil` otherwise.
   131  
   132  If `removeKey` does not exist in the IAVL tree, leftMostKey is `nil` for entire recursive stack.
   133  
   134  ##### RemovedValue
   135  
   136  RemovedValue is the value that was at the node that was removed. It does not get changed as it travels up the recursive stack.
   137  
   138  If `removeKey` does not exist in the IAVL tree, RemovedValue is `nil`.
   139  
   140  ##### Orphans
   141  
   142  Just like `recursiveSet`, any node that gets recursed upon by `recursiveRemove` in a successful `Remove` call will have to be orphaned. The Orphans list in `recursiveRemove` accumulates the list of orphans so that it can return them to `Remove`. `Remove` will then iterate through this list and add all the orphans to the mutable tree's `orphans` map.
   143  
   144  If the `removeKey` does not exist in the IAVL tree, then the orphans list is `nil`.
   145  
   146  ### Balance
   147  
   148  Anytime a node is unbalanced such that the height of its left branch and the height of its right branch differs by more than 1, the IAVL tree will rebalance itself.
   149  
   150  This is acheived by rotating the subtrees until there is no more than one height difference between two branches of any subtree in the IAVL.
   151  
   152  Since Balance is mutating the structure of the tree, any displaced nodes will be orphaned.
   153  
   154  #### RotateRight
   155  
   156  To rotate right on a node `rotatedNode`, we first orphan its left child. We clone the left child to create a new node `newNode`. We set `newNode`'s right hash and child to the `rotatedNode`. We now set `rotatedNode`'s left child to be the old right child of `newNode`.
   157  
   158  Visualization (Nodes are numbered to show correct key order is still preserved):
   159  
   160  Before `RotateRight(node8)`:
   161  ```
   162      |---9
   163  8---|
   164      |       |---7
   165      |   |---6
   166      |   |   |---5
   167      |---4
   168          |   |---3
   169          |---2
   170              |---1
   171  ```
   172  
   173  After `RotateRight(node8)`:
   174  ```
   175           |---9
   176       |---8
   177       |   |   |---7
   178       |   |---6
   179       |       |---5
   180  4'---|
   181       |   |---1
   182       |---2
   183           |---3
   184  
   185  Orphaned: 4
   186  ```
   187  
   188  Note that the key order for subtrees is still preserved.
   189  
   190  #### RotateLeft
   191  
   192  Similarly, to rotate left on a node `rotatedNode` we first orphan its right child. We clone the right child to create a new node `newNode`. We set the `newNode`'s left hash and child to the `rotatedNode`. We then set the `rotatedNode`'s right child to be the old left child of the node.
   193  
   194  Before `RotateLeft(node2)`:
   195  ```
   196              |---9
   197          |---8
   198          |   |---7
   199      |---6
   200      |   |   |---5
   201      |   |---4
   202      |       |---3
   203  2---|
   204      |---1
   205  ```
   206  
   207  After `RotateLeft(node2)`:
   208  ```
   209           |---9
   210       |---8
   211       |   |---7
   212  6'---|
   213       |       |---5
   214       |   |---4
   215       |   |   |---3  
   216       |---2
   217           |---1
   218  
   219  Orphaned: 6
   220  ```
   221  
   222  The IAVL detects whenever a subtree has become unbalanced by 2 (after any set/remove). If this does happen, then the tree is immediately rebalanced. Thus, any unbalanced subtree can only exist in 4 states:
   223  
   224  #### Left Left Case
   225  
   226  1. `RotateRight(node8)`
   227  
   228  **Before: Left Left Unbalanced**
   229  ```
   230      |---9
   231  8---|
   232      |   |---6
   233      |---4
   234          |   |---3
   235          |---2
   236  ```
   237  
   238  **After 1: Balanced**
   239  ```
   240           |---9
   241       |---8
   242       |   |---6
   243  4'---|
   244       |   |---3
   245       |---2
   246  
   247  Orphaned: 4
   248  ```
   249  
   250  #### Left Right Case
   251  
   252  Make tree left left unbalanced, and then balance.
   253  
   254  1. `RotateLeft(node4)`
   255  2. `RotateRight(node8)`
   256  
   257  **Before: Left Right Unbalanced**
   258  ```
   259      |---9
   260  8---|
   261      |   |---6
   262      |   |   |---5
   263      |---4
   264          |---2
   265  ```
   266  
   267  **After 1: Left Left Unbalanced**
   268  ```
   269      |---9
   270  8---|
   271      |---6'
   272          |   |---5
   273          |---4
   274              |---2
   275  
   276  Orphaned: 6
   277  ```
   278  
   279  **After 2: Balanced**
   280  ```
   281           |---9
   282       |---8
   283  6'---|
   284       |   |---5
   285       |---4
   286           |---2
   287  
   288  Orphaned: 6
   289  ```
   290  
   291  Note: 6 got orphaned again, so omit list repitition
   292  
   293  #### Right Right Case
   294  
   295  1. `RotateLeft(node2)`
   296  
   297  **Before: Right Right Unbalanced**
   298  ```
   299              |---9
   300          |---8
   301      |---6
   302      |   |---4
   303  2---|
   304      |---1
   305  ```
   306  
   307  **After: Balanced**
   308  ```
   309           |---9
   310       |---8
   311  6'---|
   312       |   |---4
   313       |---2
   314           |---1
   315  
   316  Orphaned: 6
   317  ```
   318  
   319  #### Right Left Case
   320  
   321  Make tree right right unbalanced, then balance.
   322  
   323  1. `RotateRight(6)`
   324  2. `RotateLeft(2)`
   325  
   326  **Before: Right Left Unbalanced**
   327  ```
   328          |---8
   329      |---6
   330      |   |---4
   331      |       |---3
   332  2---|
   333      |---1
   334  ```
   335  
   336  **After 1: Right Right Unbalanced**
   337  ```
   338              |---8
   339          |---6
   340      |---4'
   341      |   |---3
   342  2---|
   343      |---1
   344  
   345  Orphaned: 4
   346  ```
   347  
   348  **After 2: Balanced**
   349  ```
   350           |---8
   351       |---6
   352  4'---|
   353       |   |---3
   354       |---2
   355           |---1
   356  
   357  Orphaned: 4
   358  ```
   359  
   360  ### SaveVersion
   361  
   362  SaveVersion saves the current working tree as the latest version, `tree.version+1`.
   363  
   364  If the tree's root is empty, then there are no nodes to save. The `nodeDB` must still save any orphans since the root itself could be the node that has been removed since the last version. Then the `nodeDB` also saves the empty root for this version.
   365  
   366  If the root is not empty. Then SaveVersion will ensure that the `nodeDB` saves the orphans, roots and any new nodes that have been created since the last version was saved.
   367  
   368  SaveVersion also calls `nodeDB.Commit`, this ensures that any batched writes from the last save gets committed to the appropriate databases.
   369  
   370  `tree.version` gets incremented and the versions map has `versions[tree.version] = true`.
   371  
   372  It will set the lastSaved `ImmutableTree` to the current working tree, and clone the tree to allow for future updates on the next working tree. It also resets orphans to the empty map.
   373  
   374  Lastly, it returns the tree's hash, the latest version, and nil for error.
   375  
   376  SaveVersion will error if a tree at the version trying to be saved already exists.
   377  
   378  ### DeleteVersion
   379  
   380  DeleteVersion will simply call nodeDB's `DeleteVersion` function which is documented in the [nodeDB docs](./nodedb.md) and then call `nodeDB.Commit` to flush all batched updates.
   381  
   382  It will also delete the version from the versions map.
   383  
   384  DeleteVersion will return an error if the version is invalid, or nonexistent. DeleteVersion will also return an error if the version trying to be deleted is the latest version of the IAVL tree since that is unallowed.