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.