Skip to content

Commit

Permalink
feat(lib/trie): add descendants count to trie branches (#2310)
Browse files Browse the repository at this point in the history
* Note: descendants are stored in every node since we have a delete operation that cuts of an entire branch, so it's more performant to have it get the count of descendants to remove from the branch being removed, rather than iterating over this branch and all its children.
* Adapt existing tests
* Add test cases to reach back full test coverage
  • Loading branch information
qdm12 committed Mar 15, 2022
1 parent 917e1c2 commit 80998f2
Show file tree
Hide file tree
Showing 12 changed files with 962 additions and 397 deletions.
7 changes: 7 additions & 0 deletions internal/trie/node/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ type Branch struct {
// which is updated to match the trie Generation once they are
// inserted, moved or iterated over.
Generation uint64

// Statistics

// Descendants is the number of descendant nodes for
// this particular node.
Descendants uint32
}

// NewBranch creates a new branch using the arguments given.
Expand Down Expand Up @@ -62,6 +68,7 @@ func (b *Branch) StringNode() (stringNode *gotree.Node) {
stringNode.Appendf("Dirty: %t", b.Dirty)
stringNode.Appendf("Key: " + bytesToString(b.Key))
stringNode.Appendf("Value: " + bytesToString(b.Value))
stringNode.Appendf("Descendants: %d", b.Descendants)
stringNode.Appendf("Calculated encoding: " + bytesToString(b.Encoding))
stringNode.Appendf("Calculated digest: " + bytesToString(b.HashDigest))

Expand Down
19 changes: 13 additions & 6 deletions internal/trie/node/branch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,16 @@ func Test_Branch_String(t *testing.T) {
β”œβ”€β”€ Dirty: false
β”œβ”€β”€ Key: nil
β”œβ”€β”€ Value: nil
β”œβ”€β”€ Descendants: 0
β”œβ”€β”€ Calculated encoding: nil
└── Calculated digest: nil`,
},
"branch with value smaller than 1024": {
branch: &Branch{
Key: []byte{1, 2},
Value: []byte{3, 4},
Dirty: true,
Key: []byte{1, 2},
Value: []byte{3, 4},
Dirty: true,
Descendants: 3,
Children: [16]Node{
nil, nil, nil,
&Leaf{},
Expand All @@ -105,6 +107,7 @@ func Test_Branch_String(t *testing.T) {
β”œβ”€β”€ Dirty: true
β”œβ”€β”€ Key: 0x0102
β”œβ”€β”€ Value: 0x0304
β”œβ”€β”€ Descendants: 3
β”œβ”€β”€ Calculated encoding: nil
β”œβ”€β”€ Calculated digest: nil
β”œβ”€β”€ Child 3
Expand All @@ -121,6 +124,7 @@ func Test_Branch_String(t *testing.T) {
| β”œβ”€β”€ Dirty: false
| β”œβ”€β”€ Key: nil
| β”œβ”€β”€ Value: nil
| β”œβ”€β”€ Descendants: 0
| β”œβ”€β”€ Calculated encoding: nil
| └── Calculated digest: nil
└── Child 11
Expand All @@ -134,9 +138,10 @@ func Test_Branch_String(t *testing.T) {
},
"branch with value higher than 1024": {
branch: &Branch{
Key: []byte{1, 2},
Value: make([]byte, 1025),
Dirty: true,
Key: []byte{1, 2},
Value: make([]byte, 1025),
Dirty: true,
Descendants: 3,
Children: [16]Node{
nil, nil, nil,
&Leaf{},
Expand All @@ -152,6 +157,7 @@ func Test_Branch_String(t *testing.T) {
β”œβ”€β”€ Dirty: true
β”œβ”€β”€ Key: 0x0102
β”œβ”€β”€ Value: 0x0000000000000000...0000000000000000
β”œβ”€β”€ Descendants: 3
β”œβ”€β”€ Calculated encoding: nil
β”œβ”€β”€ Calculated digest: nil
β”œβ”€β”€ Child 3
Expand All @@ -168,6 +174,7 @@ func Test_Branch_String(t *testing.T) {
| β”œβ”€β”€ Dirty: false
| β”œβ”€β”€ Key: nil
| β”œβ”€β”€ Value: nil
| β”œβ”€β”€ Descendants: 0
| β”œβ”€β”€ Calculated encoding: nil
| └── Calculated digest: nil
└── Child 11
Expand Down
5 changes: 3 additions & 2 deletions internal/trie/node/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ package node
// children as well.
func (b *Branch) Copy(copyChildren bool) Node {
cpy := &Branch{
Dirty: b.Dirty,
Generation: b.Generation,
Dirty: b.Dirty,
Generation: b.Generation,
Descendants: b.GetDescendants(),
}

if copyChildren {
Expand Down
2 changes: 2 additions & 0 deletions internal/trie/node/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ func decodeBranch(reader io.Reader, header byte) (branch *Branch, err error) {
if (childrenBitmap[i/8]>>(i%8))&1 != 1 {
continue
}
branch.AddDescendants(1)

var hash []byte
err := sd.Decode(&hash)
if err != nil {
Expand Down
6 changes: 4 additions & 2 deletions internal/trie/node/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ func Test_decodeBranch(t *testing.T) {
HashDigest: []byte{1, 2, 3, 4, 5},
},
},
Dirty: true,
Dirty: true,
Descendants: 1,
},
},
"value decoding error for node type 3": {
Expand Down Expand Up @@ -211,7 +212,8 @@ func Test_decodeBranch(t *testing.T) {
HashDigest: []byte{1, 2, 3, 4, 5},
},
},
Dirty: true,
Dirty: true,
Descendants: 1,
},
},
}
Expand Down
3 changes: 2 additions & 1 deletion internal/trie/node/encode_decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ func Test_Branch_Encode_Decode(t *testing.T) {
HashDigest: []byte{0x41, 0x9, 0x4, 0xa},
},
},
Dirty: true,
Dirty: true,
Descendants: 1,
},
},
}
Expand Down
19 changes: 19 additions & 0 deletions internal/trie/node/stats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2022 ChainSafe Systems (ON)
// SPDX-License-Identifier: LGPL-3.0-only

package node

// GetDescendants returns the number of descendants in the branch.
func (b *Branch) GetDescendants() (descendants uint32) {
return b.Descendants
}

// AddDescendants adds descendant nodes count to the node stats.
func (b *Branch) AddDescendants(n uint32) {
b.Descendants += n
}

// SubDescendants subtracts descendant nodes count from the node stats.
func (b *Branch) SubDescendants(n uint32) {
b.Descendants -= n
}
60 changes: 60 additions & 0 deletions internal/trie/node/stats_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2022 ChainSafe Systems (ON)
// SPDX-License-Identifier: LGPL-3.0-only

package node

import (
"testing"

"github.com/stretchr/testify/assert"
)

func Test_Branch_GetDescendants(t *testing.T) {
t.Parallel()

const descendants uint32 = 10
branch := &Branch{
Descendants: descendants,
}
result := branch.GetDescendants()

assert.Equal(t, descendants, result)
}

func Test_Branch_AddDescendants(t *testing.T) {
t.Parallel()

const (
initialDescendants uint32 = 10
addDescendants uint32 = 2
finalDescendants uint32 = 12
)
branch := &Branch{
Descendants: initialDescendants,
}
branch.AddDescendants(addDescendants)
expected := &Branch{
Descendants: finalDescendants,
}

assert.Equal(t, expected, branch)
}

func Test_Branch_SubDescendants(t *testing.T) {
t.Parallel()

const (
initialDescendants uint32 = 10
subDescendants uint32 = 2
finalDescendants uint32 = 8
)
branch := &Branch{
Descendants: initialDescendants,
}
branch.SubDescendants(subDescendants)
expected := &Branch{
Descendants: finalDescendants,
}

assert.Equal(t, expected, branch)
}
6 changes: 4 additions & 2 deletions lib/trie/print_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ func Test_Trie_String(t *testing.T) {
"branch root": {
trie: Trie{
root: &node.Branch{
Key: nil,
Value: []byte{1, 2},
Key: nil,
Value: []byte{1, 2},
Descendants: 2,
Children: [16]node.Node{
&node.Leaf{
Key: []byte{1, 2, 3},
Expand All @@ -61,6 +62,7 @@ func Test_Trie_String(t *testing.T) {
β”œβ”€β”€ Dirty: false
β”œβ”€β”€ Key: nil
β”œβ”€β”€ Value: 0x0102
β”œβ”€β”€ Descendants: 2
β”œβ”€β”€ Calculated encoding: nil
β”œβ”€β”€ Calculated digest: nil
β”œβ”€β”€ Child 0
Expand Down
Loading

0 comments on commit 80998f2

Please sign in to comment.