Skip to content

Commit 86624cf

Browse files
authored
feat(lib/trie): only copy nodes when mutation is certain (#2352)
- Only copy nodes when mutation is certain - Should alleviate memory operations and garbage collection load - Trie snapshot helper methods `prepLeafForMutation` and `prepBranchForMutation` - Call `SetDirty(true)` in snapshot trie helper methods - Add test case to reach back full coverage of trie.go - Note there is still full unit test coverage (from `trie_test.go`) making these changes possible/safe.
1 parent 846bb47 commit 86624cf

File tree

2 files changed

+94
-125
lines changed

2 files changed

+94
-125
lines changed

lib/trie/trie.go

Lines changed: 72 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,37 @@ func (t *Trie) Snapshot() (newTrie *Trie) {
6262
}
6363
}
6464

65+
func (t *Trie) prepLeafForMutation(currentLeaf *node.Leaf) (newLeaf *node.Leaf) {
66+
if currentLeaf.Generation == t.generation {
67+
// no need to deep copy and update generation
68+
// of current leaf.
69+
newLeaf = currentLeaf
70+
} else {
71+
newNode := updateGeneration(currentLeaf, t.generation, t.deletedKeys)
72+
newLeaf = newNode.(*node.Leaf)
73+
}
74+
newLeaf.SetDirty(true)
75+
return newLeaf
76+
}
77+
78+
func (t *Trie) prepBranchForMutation(currentBranch *node.Branch) (newBranch *node.Branch) {
79+
if currentBranch.Generation == t.generation {
80+
// no need to deep copy and update generation
81+
// of current branch.
82+
newBranch = currentBranch
83+
} else {
84+
newNode := updateGeneration(currentBranch, t.generation, t.deletedKeys)
85+
newBranch = newNode.(*node.Branch)
86+
}
87+
newBranch.SetDirty(true)
88+
return newBranch
89+
}
90+
6591
// updateGeneration is called when the currentNode is from
6692
// an older trie generation (snapshot) so we deep copy the
6793
// node and update the generation on the newer copy.
6894
func updateGeneration(currentNode Node, trieGeneration uint64,
6995
deletedHashes map[common.Hash]struct{}) (newNode Node) {
70-
if currentNode.GetGeneration() == trieGeneration {
71-
panic(fmt.Sprintf(
72-
"current node has the same generation %d as the trie generation, "+
73-
"make sure the caller properly checks for the node generation to "+
74-
"be smaller than the trie generation.", trieGeneration))
75-
}
7696
const copyChildren = false
7797
newNode = currentNode.Copy(copyChildren)
7898
newNode.SetGeneration(trieGeneration)
@@ -322,29 +342,26 @@ func (t *Trie) insert(parent Node, key, value []byte) (newParent Node) {
322342
}
323343

324344
// TODO ensure all values have dirty set to true
325-
newParent = parent
326-
if parent.GetGeneration() < t.generation {
327-
newParent = updateGeneration(parent, t.generation, t.deletedKeys)
328-
}
329345

330-
switch newParent.Type() {
346+
switch parent.Type() {
331347
case node.BranchType, node.BranchWithValueType:
332-
parentBranch := newParent.(*node.Branch)
348+
parentBranch := parent.(*node.Branch)
333349
return t.insertInBranch(parentBranch, key, value)
334350
default:
335-
parentLeaf := newParent.(*node.Leaf)
351+
parentLeaf := parent.(*node.Leaf)
336352
return t.insertInLeaf(parentLeaf, key, value)
337353
}
338354
}
339355

340356
func (t *Trie) insertInLeaf(parentLeaf *node.Leaf, key,
341357
value []byte) (newParent Node) {
342358
if bytes.Equal(parentLeaf.Key, key) {
343-
if !bytes.Equal(value, parentLeaf.Value) {
344-
parentLeaf.Value = value
345-
parentLeaf.Generation = t.generation
346-
parentLeaf.SetDirty(true)
359+
if bytes.Equal(value, parentLeaf.Value) {
360+
return parentLeaf
347361
}
362+
363+
parentLeaf = t.prepLeafForMutation(parentLeaf)
364+
parentLeaf.Value = value
348365
return parentLeaf
349366
}
350367

@@ -364,9 +381,9 @@ func (t *Trie) insertInLeaf(parentLeaf *node.Leaf, key,
364381

365382
if len(key) < len(parentLeafKey) {
366383
// Move the current leaf parent as a child to the new branch.
384+
parentLeaf = t.prepLeafForMutation(parentLeaf)
367385
childIndex := parentLeafKey[commonPrefixLength]
368386
parentLeaf.Key = parentLeaf.Key[commonPrefixLength+1:]
369-
parentLeaf.SetDirty(true)
370387
newBranchParent.Children[childIndex] = parentLeaf
371388
}
372389

@@ -378,9 +395,9 @@ func (t *Trie) insertInLeaf(parentLeaf *node.Leaf, key,
378395
newBranchParent.Value = parentLeaf.Value
379396
} else {
380397
// make the leaf a child of the new branch
398+
parentLeaf = t.prepLeafForMutation(parentLeaf)
381399
childIndex := parentLeafKey[commonPrefixLength]
382400
parentLeaf.Key = parentLeaf.Key[commonPrefixLength+1:]
383-
parentLeaf.SetDirty(true)
384401
newBranchParent.Children[childIndex] = parentLeaf
385402
}
386403
childIndex := key[commonPrefixLength]
@@ -395,9 +412,9 @@ func (t *Trie) insertInLeaf(parentLeaf *node.Leaf, key,
395412
}
396413

397414
func (t *Trie) insertInBranch(parentBranch *node.Branch, key, value []byte) (newParent Node) {
415+
parentBranch = t.prepBranchForMutation(parentBranch)
416+
398417
if bytes.Equal(key, parentBranch.Key) {
399-
parentBranch.SetDirty(true)
400-
parentBranch.Generation = t.generation
401418
parentBranch.Value = value
402419
return parentBranch
403420
}
@@ -418,12 +435,9 @@ func (t *Trie) insertInBranch(parentBranch *node.Branch, key, value []byte) (new
418435
}
419436
} else {
420437
child = t.insert(child, remainingKey, value)
421-
child.SetDirty(true)
422438
}
423439

424440
parentBranch.Children[childIndex] = child
425-
parentBranch.SetDirty(true)
426-
parentBranch.Generation = t.generation
427441
return parentBranch
428442
}
429443

@@ -435,13 +449,11 @@ func (t *Trie) insertInBranch(parentBranch *node.Branch, key, value []byte) (new
435449
Generation: t.generation,
436450
Dirty: true,
437451
}
438-
parentBranch.SetDirty(true)
439452

440453
oldParentIndex := parentBranch.Key[commonPrefixLength]
441454
remainingOldParentKey := parentBranch.Key[commonPrefixLength+1:]
442455

443456
parentBranch.Key = remainingOldParentKey
444-
parentBranch.Generation = t.generation
445457
newParentBranch.Children[oldParentIndex] = parentBranch
446458

447459
if len(key) <= commonPrefixLength {
@@ -653,36 +665,20 @@ func (t *Trie) clearPrefixLimit(parent Node, prefix []byte, limit uint32) (
653665
return nil, 0, true
654666
}
655667

656-
newParent = parent
657-
if parent.GetGeneration() < t.generation {
658-
newParent = updateGeneration(parent, t.generation, t.deletedKeys)
659-
}
660-
661-
if newParent.Type() == node.LeafType {
662-
leaf := newParent.(*node.Leaf)
668+
if parent.Type() == node.LeafType {
669+
leaf := parent.(*node.Leaf)
663670
// if prefix is not found, it's also all deleted.
664671
// TODO check this is the same behaviour as in substrate
665672
const allDeleted = true
666673
if bytes.HasPrefix(leaf.Key, prefix) {
667674
valuesDeleted = 1
668675
return nil, valuesDeleted, allDeleted
669676
}
670-
// not modified so return the leaf of the original
671-
// trie generation. The copied leaf newParent will be
672-
// garbage collected.
673677
return parent, 0, allDeleted
674678
}
675679

676-
branch := newParent.(*node.Branch)
677-
newParent, valuesDeleted, allDeleted = t.clearPrefixLimitBranch(branch, prefix, limit)
678-
if valuesDeleted == 0 {
679-
// not modified so return the node of the original
680-
// trie generation. The copied newParent will be
681-
// garbage collected.
682-
newParent = parent
683-
}
684-
685-
return newParent, valuesDeleted, allDeleted
680+
branch := parent.(*node.Branch)
681+
return t.clearPrefixLimitBranch(branch, prefix, limit)
686682
}
687683

688684
func (t *Trie) clearPrefixLimitBranch(branch *node.Branch, prefix []byte, limit uint32) (
@@ -714,13 +710,14 @@ func (t *Trie) clearPrefixLimitBranch(branch *node.Branch, prefix []byte, limit
714710
childPrefix := prefix[len(branch.Key)+1:]
715711
child := branch.Children[childIndex]
716712

717-
newParent = branch // mostly just a reminder for the reader
718-
branch.Children[childIndex], valuesDeleted, allDeleted = t.clearPrefixLimit(child, childPrefix, limit)
719-
if valuesDeleted > 0 {
720-
branch.SetDirty(true)
721-
newParent = handleDeletion(branch, prefix)
713+
child, valuesDeleted, allDeleted = t.clearPrefixLimit(child, childPrefix, limit)
714+
if valuesDeleted == 0 {
715+
return branch, valuesDeleted, allDeleted
722716
}
723717

718+
branch = t.prepBranchForMutation(branch)
719+
branch.Children[childIndex] = child
720+
newParent = handleDeletion(branch, prefix)
724721
return newParent, valuesDeleted, allDeleted
725722
}
726723

@@ -738,11 +735,16 @@ func (t *Trie) clearPrefixLimitChild(branch *node.Branch, prefix []byte, limit u
738735
}
739736

740737
nilPrefix := ([]byte)(nil)
741-
branch.Children[childIndex], valuesDeleted = t.deleteNodesLimit(child, nilPrefix, limit)
742-
branch.SetDirty(true)
738+
child, valuesDeleted = t.deleteNodesLimit(child, nilPrefix, limit)
739+
if valuesDeleted == 0 {
740+
allDeleted = branch.Children[childIndex] == nil
741+
return branch, valuesDeleted, allDeleted
742+
}
743743

744-
newParent = handleDeletion(branch, prefix)
744+
branch = t.prepBranchForMutation(branch)
745+
branch.Children[childIndex] = child
745746

747+
newParent = handleDeletion(branch, prefix)
746748
allDeleted = branch.Children[childIndex] == nil
747749
return newParent, valuesDeleted, allDeleted
748750
}
@@ -757,17 +759,12 @@ func (t *Trie) deleteNodesLimit(parent Node, prefix []byte, limit uint32) (
757759
return nil, 0
758760
}
759761

760-
newParent = parent
761-
if parent.GetGeneration() < t.generation {
762-
newParent = updateGeneration(parent, t.generation, t.deletedKeys)
763-
}
764-
765-
if newParent.Type() == node.LeafType {
762+
if parent.Type() == node.LeafType {
766763
valuesDeleted = 1
767764
return nil, valuesDeleted
768765
}
769766

770-
branch := newParent.(*node.Branch)
767+
branch := parent.(*node.Branch)
771768

772769
fullKey := concatenateSlices(prefix, branch.Key)
773770

@@ -779,14 +776,14 @@ func (t *Trie) deleteNodesLimit(parent Node, prefix []byte, limit uint32) (
779776
continue
780777
}
781778

779+
branch = t.prepBranchForMutation(branch)
782780
branch.Children[i], newDeleted = t.deleteNodesLimit(child, fullKey, limit)
783781
if branch.Children[i] == nil {
784782
nilChildren++
785783
}
786784
limit -= newDeleted
787785
valuesDeleted += newDeleted
788786

789-
branch.SetDirty(true)
790787
newParent = handleDeletion(branch, fullKey)
791788
if nilChildren == node.ChildrenCapacity &&
792789
branch.Value == nil {
@@ -825,23 +822,15 @@ func (t *Trie) clearPrefix(parent Node, prefix []byte) (
825822
return nil, false
826823
}
827824

828-
newParent = parent
829-
if parent.GetGeneration() < t.generation {
830-
newParent = updateGeneration(parent, t.generation, t.deletedKeys)
831-
}
832-
833-
if bytes.HasPrefix(newParent.GetKey(), prefix) {
825+
if bytes.HasPrefix(parent.GetKey(), prefix) {
834826
return nil, true
835827
}
836828

837-
if newParent.Type() == node.LeafType {
838-
// not modified so return the leaf of the original
839-
// trie generation. The copied newParent will be
840-
// garbage collected.
829+
if parent.Type() == node.LeafType {
841830
return parent, false
842831
}
843832

844-
branch := newParent.(*node.Branch)
833+
branch := parent.(*node.Branch)
845834

846835
if len(prefix) == len(branch.Key)+1 &&
847836
bytes.HasPrefix(branch.Key, prefix[:len(prefix)-1]) {
@@ -850,41 +839,32 @@ func (t *Trie) clearPrefix(parent Node, prefix []byte) (
850839
child := branch.Children[childIndex]
851840

852841
if child == nil {
853-
// child is already nil at the child index
854-
// node is not modified so return the branch of the original
855-
// trie generation. The copied newParent will be
856-
// garbage collected.
857842
return parent, false
858843
}
859844

845+
branch = t.prepBranchForMutation(branch)
860846
branch.Children[childIndex] = nil
861-
branch.SetDirty(true)
862847
newParent = handleDeletion(branch, prefix)
863848
return newParent, true
864849
}
865850

866851
noPrefixForNode := len(prefix) <= len(branch.Key) ||
867852
lenCommonPrefix(branch.Key, prefix) < len(branch.Key)
868853
if noPrefixForNode {
869-
// not modified so return the branch of the original
870-
// trie generation. The copied newParent will be
871-
// garbage collected.
872854
return parent, false
873855
}
874856

875857
childIndex := prefix[len(branch.Key)]
876858
childPrefix := prefix[len(branch.Key)+1:]
877859
child := branch.Children[childIndex]
878860

879-
branch.Children[childIndex], updated = t.clearPrefix(child, childPrefix)
861+
child, updated = t.clearPrefix(child, childPrefix)
880862
if !updated {
881-
// branch not modified so return the branch of the original
882-
// trie generation. The copied newParent will be
883-
// garbage collected.
884863
return parent, false
885864
}
886865

887-
branch.SetDirty(true)
866+
branch = t.prepBranchForMutation(branch)
867+
branch.Children[childIndex] = child
888868
newParent = handleDeletion(branch, prefix)
889869
return newParent, true
890870
}
@@ -902,32 +882,15 @@ func (t *Trie) delete(parent Node, key []byte) (newParent Node, deleted bool) {
902882
return nil, false
903883
}
904884

905-
newParent = parent
906-
if parent.GetGeneration() < t.generation {
907-
newParent = updateGeneration(parent, t.generation, t.deletedKeys)
908-
}
909-
910-
if newParent.Type() == node.LeafType {
911-
newParent = deleteLeaf(newParent, key)
912-
if newParent == nil {
885+
if parent.Type() == node.LeafType {
886+
if deleteLeaf(parent, key) == nil {
913887
return nil, true
914888
}
915-
// The leaf was not deleted so return the original
916-
// parent without its generation updated.
917-
// The copied newParent will be garbage collected.
918889
return parent, false
919890
}
920891

921-
branch := newParent.(*node.Branch)
922-
newParent, deleted = t.deleteBranch(branch, key)
923-
if !deleted {
924-
// Nothing was deleted so return the original
925-
// parent without its generation updated.
926-
// The copied newParent will be garbage collected.
927-
return parent, false
928-
}
929-
930-
return newParent, true
892+
branch := parent.(*node.Branch)
893+
return t.deleteBranch(branch, key)
931894
}
932895

933896
func deleteLeaf(parent Node, key []byte) (newParent Node) {
@@ -939,8 +902,8 @@ func deleteLeaf(parent Node, key []byte) (newParent Node) {
939902

940903
func (t *Trie) deleteBranch(branch *node.Branch, key []byte) (newParent Node, deleted bool) {
941904
if len(key) == 0 || bytes.Equal(branch.Key, key) {
905+
branch = t.prepBranchForMutation(branch)
942906
branch.Value = nil
943-
branch.SetDirty(true)
944907
return handleDeletion(branch, key), true
945908
}
946909

@@ -954,8 +917,8 @@ func (t *Trie) deleteBranch(branch *node.Branch, key []byte) (newParent Node, de
954917
return branch, false
955918
}
956919

920+
branch = t.prepBranchForMutation(branch)
957921
branch.Children[childIndex] = newChild
958-
branch.SetDirty(true)
959922
newParent = handleDeletion(branch, key)
960923
return newParent, true
961924
}

0 commit comments

Comments
 (0)