Skip to content

Commit 26868df

Browse files
authored
fix(lib/trie): prepare trie nodes for mutation only when needed (#2834)
- Only prepare nodes for mutation if mutation is guaranteed - Fixes 'reported deleted hashes' when no mutation is done, indirectly fixing the online pruning that would previously prune database keys that may still be in use. - Add comment on `deletedKeys` trie struct field - Update tests to maintain full test coverage on trie code
1 parent c01c4a3 commit 26868df

File tree

4 files changed

+197
-29
lines changed

4 files changed

+197
-29
lines changed

internal/trie/node/subvalue.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2022 ChainSafe Systems (ON)
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
4+
package node
5+
6+
import "bytes"
7+
8+
// SubValueEqual returns true if the node subvalue is equal to the
9+
// subvalue given as argument. In particular, it returns false
10+
// if one subvalue is nil and the other subvalue is the empty slice.
11+
func (n Node) SubValueEqual(subValue []byte) (equal bool) {
12+
if len(subValue) == 0 && len(n.SubValue) == 0 {
13+
return (subValue == nil && n.SubValue == nil) ||
14+
(subValue != nil && n.SubValue != nil)
15+
}
16+
return bytes.Equal(n.SubValue, subValue)
17+
}

internal/trie/node/subvalue_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2022 ChainSafe Systems (ON)
2+
// SPDX-License-Identifier: LGPL-3.0-only
3+
4+
package node
5+
6+
import (
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func Test_Node_SubValueEqual(t *testing.T) {
13+
t.Parallel()
14+
15+
testCases := map[string]struct {
16+
node Node
17+
subValue []byte
18+
equal bool
19+
}{
20+
"nil node subvalue and nil subvalue": {
21+
equal: true,
22+
},
23+
"empty node subvalue and empty subvalue": {
24+
node: Node{SubValue: []byte{}},
25+
subValue: []byte{},
26+
equal: true,
27+
},
28+
"nil node subvalue and empty subvalue": {
29+
subValue: []byte{},
30+
},
31+
"empty node subvalue and nil subvalue": {
32+
node: Node{SubValue: []byte{}},
33+
},
34+
"equal non empty values": {
35+
node: Node{SubValue: []byte{1, 2}},
36+
subValue: []byte{1, 2},
37+
equal: true,
38+
},
39+
"not equal non empty values": {
40+
node: Node{SubValue: []byte{1, 2}},
41+
subValue: []byte{1, 3},
42+
},
43+
}
44+
45+
for name, testCase := range testCases {
46+
testCase := testCase
47+
t.Run(name, func(t *testing.T) {
48+
t.Parallel()
49+
50+
node := testCase.node
51+
52+
equal := node.SubValueEqual(testCase.subValue)
53+
54+
assert.Equal(t, testCase.equal, equal)
55+
})
56+
}
57+
}

lib/trie/trie.go

Lines changed: 61 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ var EmptyHash, _ = NewEmptyTrie().Hash()
1818

1919
// Trie is a base 16 modified Merkle Patricia trie.
2020
type Trie struct {
21-
generation uint64
22-
root *Node
23-
childTries map[common.Hash]*Trie
21+
generation uint64
22+
root *Node
23+
childTries map[common.Hash]*Trie
24+
// deletedMerkleValues are the node Merkle values that were deleted
25+
// from this trie since the last snapshot. These are used by the online
26+
// pruner to detect with database keys (trie node Merkle values) can
27+
// be deleted.
2428
deletedMerkleValues map[string]struct{}
2529
}
2630

@@ -319,20 +323,22 @@ func findNextKeyChild(children []*Node, startIndex byte,
319323
// key specified in little Endian format.
320324
func (t *Trie) Put(keyLE, value []byte) {
321325
nibblesKey := codec.KeyLEToNibbles(keyLE)
322-
t.root, _ = t.insert(t.root, nibblesKey, value)
326+
t.root, _, _ = t.insert(t.root, nibblesKey, value)
323327
}
324328

325329
// insert inserts a value in the trie at the key specified.
326330
// It may create one or more new nodes or update an existing node.
327-
func (t *Trie) insert(parent *Node, key, value []byte) (newParent *Node, nodesCreated uint32) {
331+
func (t *Trie) insert(parent *Node, key, value []byte) (
332+
newParent *Node, mutated bool, nodesCreated uint32) {
328333
if parent == nil {
329-
const nodesCreated = 1
334+
mutated = true
335+
nodesCreated = 1
330336
return &Node{
331337
Key: key,
332338
SubValue: value,
333339
Generation: t.generation,
334340
Dirty: true,
335-
}, nodesCreated
341+
}, mutated, nodesCreated
336342
}
337343

338344
// TODO ensure all values have dirty set to true
@@ -344,23 +350,26 @@ func (t *Trie) insert(parent *Node, key, value []byte) (newParent *Node, nodesCr
344350
}
345351

346352
func (t *Trie) insertInLeaf(parentLeaf *Node, key, value []byte) (
347-
newParent *Node, nodesCreated uint32) {
353+
newParent *Node, mutated bool, nodesCreated uint32) {
348354
if bytes.Equal(parentLeaf.Key, key) {
349355
nodesCreated = 0
350-
if bytes.Equal(value, parentLeaf.SubValue) {
351-
return parentLeaf, nodesCreated
356+
if parentLeaf.SubValueEqual(value) {
357+
mutated = false
358+
return parentLeaf, mutated, nodesCreated
352359
}
353360

354361
copySettings := node.DefaultCopySettings
355362
copySettings.CopyValue = false
356363
parentLeaf = t.prepLeafForMutation(parentLeaf, copySettings)
357364
parentLeaf.SubValue = value
358-
return parentLeaf, nodesCreated
365+
mutated = true
366+
return parentLeaf, mutated, nodesCreated
359367
}
360368

361369
commonPrefixLength := lenCommonPrefix(key, parentLeaf.Key)
362370

363371
// Convert the current leaf parent into a branch parent
372+
mutated = true
364373
newBranchParent := &Node{
365374
Key: key[:commonPrefixLength],
366375
Generation: t.generation,
@@ -376,15 +385,18 @@ func (t *Trie) insertInLeaf(parentLeaf *Node, key, value []byte) (
376385
if len(key) < len(parentLeafKey) {
377386
// Move the current leaf parent as a child to the new branch.
378387
copySettings := node.DefaultCopySettings
379-
parentLeaf = t.prepLeafForMutation(parentLeaf, copySettings)
380388
childIndex := parentLeafKey[commonPrefixLength]
381-
parentLeaf.Key = parentLeaf.Key[commonPrefixLength+1:]
389+
newParentLeafKey := parentLeaf.Key[commonPrefixLength+1:]
390+
if !bytes.Equal(parentLeaf.Key, newParentLeafKey) {
391+
parentLeaf = t.prepLeafForMutation(parentLeaf, copySettings)
392+
parentLeaf.Key = newParentLeafKey
393+
}
382394
newBranchParent.Children[childIndex] = parentLeaf
383395
newBranchParent.Descendants++
384396
nodesCreated++
385397
}
386398

387-
return newBranchParent, nodesCreated
399+
return newBranchParent, mutated, nodesCreated
388400
}
389401

390402
if len(parentLeaf.Key) == commonPrefixLength {
@@ -393,9 +405,12 @@ func (t *Trie) insertInLeaf(parentLeaf *Node, key, value []byte) (
393405
} else {
394406
// make the leaf a child of the new branch
395407
copySettings := node.DefaultCopySettings
396-
parentLeaf = t.prepLeafForMutation(parentLeaf, copySettings)
397408
childIndex := parentLeafKey[commonPrefixLength]
398-
parentLeaf.Key = parentLeaf.Key[commonPrefixLength+1:]
409+
newParentLeafKey := parentLeaf.Key[commonPrefixLength+1:]
410+
if !bytes.Equal(parentLeaf.Key, newParentLeafKey) {
411+
parentLeaf = t.prepLeafForMutation(parentLeaf, copySettings)
412+
parentLeaf.Key = newParentLeafKey
413+
}
399414
newBranchParent.Children[childIndex] = parentLeaf
400415
newBranchParent.Descendants++
401416
nodesCreated++
@@ -410,17 +425,22 @@ func (t *Trie) insertInLeaf(parentLeaf *Node, key, value []byte) (
410425
newBranchParent.Descendants++
411426
nodesCreated++
412427

413-
return newBranchParent, nodesCreated
428+
return newBranchParent, mutated, nodesCreated
414429
}
415430

416431
func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte) (
417-
newParent *Node, nodesCreated uint32) {
432+
newParent *Node, mutated bool, nodesCreated uint32) {
418433
copySettings := node.DefaultCopySettings
419-
parentBranch = t.prepBranchForMutation(parentBranch, copySettings)
420434

421435
if bytes.Equal(key, parentBranch.Key) {
436+
if parentBranch.SubValueEqual(value) {
437+
mutated = false
438+
return parentBranch, mutated, 0
439+
}
440+
parentBranch = t.prepBranchForMutation(parentBranch, copySettings)
422441
parentBranch.SubValue = value
423-
return parentBranch, 0
442+
mutated = true
443+
return parentBranch, mutated, 0
424444
}
425445

426446
if bytes.HasPrefix(key, parentBranch.Key) {
@@ -438,17 +458,27 @@ func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte) (
438458
Dirty: true,
439459
}
440460
nodesCreated = 1
441-
} else {
442-
child, nodesCreated = t.insert(child, remainingKey, value)
461+
parentBranch = t.prepBranchForMutation(parentBranch, copySettings)
462+
parentBranch.Children[childIndex] = child
463+
parentBranch.Descendants += nodesCreated
464+
mutated = true
465+
return parentBranch, mutated, nodesCreated
466+
}
467+
468+
child, mutated, nodesCreated = t.insert(child, remainingKey, value)
469+
if !mutated {
470+
return parentBranch, mutated, 0
443471
}
444472

473+
parentBranch = t.prepBranchForMutation(parentBranch, copySettings)
445474
parentBranch.Children[childIndex] = child
446475
parentBranch.Descendants += nodesCreated
447-
return parentBranch, nodesCreated
476+
return parentBranch, mutated, nodesCreated
448477
}
449478

450479
// we need to branch out at the point where the keys diverge
451480
// update partial keys, new branch has key up to matching length
481+
mutated = true
452482
nodesCreated = 1
453483
commonPrefixLength := lenCommonPrefix(key, parentBranch.Key)
454484
newParentBranch := &Node{
@@ -461,6 +491,8 @@ func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte) (
461491
oldParentIndex := parentBranch.Key[commonPrefixLength]
462492
remainingOldParentKey := parentBranch.Key[commonPrefixLength+1:]
463493

494+
// Note: parentBranch.Key != remainingOldParentKey
495+
parentBranch = t.prepBranchForMutation(parentBranch, copySettings)
464496
parentBranch.Key = remainingOldParentKey
465497
newParentBranch.Children[oldParentIndex] = parentBranch
466498
newParentBranch.Descendants += 1 + parentBranch.Descendants
@@ -471,12 +503,12 @@ func (t *Trie) insertInBranch(parentBranch *Node, key, value []byte) (
471503
childIndex := key[commonPrefixLength]
472504
remainingKey := key[commonPrefixLength+1:]
473505
var additionalNodesCreated uint32
474-
newParentBranch.Children[childIndex], additionalNodesCreated = t.insert(nil, remainingKey, value)
506+
newParentBranch.Children[childIndex], _, additionalNodesCreated = t.insert(nil, remainingKey, value)
475507
nodesCreated += additionalNodesCreated
476508
newParentBranch.Descendants += additionalNodesCreated
477509
}
478510

479-
return newParentBranch, nodesCreated
511+
return newParentBranch, mutated, nodesCreated
480512
}
481513

482514
// LoadFromMap loads the given data mapping of key to value into the trie.
@@ -792,7 +824,11 @@ func (t *Trie) deleteNodesLimit(parent *Node, limit uint32) (
792824

793825
copySettings := node.DefaultCopySettings
794826
branch = t.prepBranchForMutation(branch, copySettings)
827+
795828
branch.Children[i], newDeleted, newNodesRemoved = t.deleteNodesLimit(child, limit)
829+
// Note: newDeleted can never be zero here since the limit isn't zero
830+
// and the child is not nil. Therefore it is safe to prepare the branch
831+
// for mutation right before this call.
796832
if branch.Children[i] == nil {
797833
nilChildren++
798834
}

0 commit comments

Comments
 (0)