Skip to content

Commit

Permalink
fix(trie): no in-memory caching of node encoding (#2919)
Browse files Browse the repository at this point in the history
  • Loading branch information
qdm12 authored Nov 11, 2022
1 parent d769d1c commit 856780b
Show file tree
Hide file tree
Showing 13 changed files with 40 additions and 306 deletions.
5 changes: 0 additions & 5 deletions internal/trie/node/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,6 @@ func (n *Node) Copy(settings CopySettings) *Node {
cpy.MerkleValue = make([]byte, len(n.MerkleValue))
copy(cpy.MerkleValue, n.MerkleValue)
}

if n.Encoding != nil {
cpy.Encoding = make([]byte, len(n.Encoding))
copy(cpy.Encoding, n.Encoding)
}
}

return cpy
Expand Down
7 changes: 0 additions & 7 deletions internal/trie/node/copy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ func Test_Node_Copy(t *testing.T) {
}),
Dirty: true,
MerkleValue: []byte{5},
Encoding: []byte{6},
},
settings: DefaultCopySettings,
expectedNode: &Node{
Expand Down Expand Up @@ -96,7 +95,6 @@ func Test_Node_Copy(t *testing.T) {
}),
Dirty: true,
MerkleValue: []byte{5},
Encoding: []byte{6},
},
settings: DeepCopySettings,
expectedNode: &Node{
Expand All @@ -110,7 +108,6 @@ func Test_Node_Copy(t *testing.T) {
}),
Dirty: true,
MerkleValue: []byte{5},
Encoding: []byte{6},
},
},
"non empty leaf": {
Expand All @@ -119,7 +116,6 @@ func Test_Node_Copy(t *testing.T) {
SubValue: []byte{3, 4},
Dirty: true,
MerkleValue: []byte{5},
Encoding: []byte{6},
},
settings: DefaultCopySettings,
expectedNode: &Node{
Expand All @@ -134,15 +130,13 @@ func Test_Node_Copy(t *testing.T) {
SubValue: []byte{3, 4},
Dirty: true,
MerkleValue: []byte{5},
Encoding: []byte{6},
},
settings: DeepCopySettings,
expectedNode: &Node{
Key: []byte{1, 2},
SubValue: []byte{3, 4},
Dirty: true,
MerkleValue: []byte{5},
Encoding: []byte{6},
},
},
}
Expand All @@ -158,7 +152,6 @@ func Test_Node_Copy(t *testing.T) {
testForSliceModif(t, testCase.node.Key, nodeCopy.Key)
testForSliceModif(t, testCase.node.SubValue, nodeCopy.SubValue)
testForSliceModif(t, testCase.node.MerkleValue, nodeCopy.MerkleValue)
testForSliceModif(t, testCase.node.Encoding, nodeCopy.Encoding)

if testCase.node.Kind() == Branch {
testCase.node.Children[15] = &Node{Key: []byte("modified")}
Expand Down
4 changes: 1 addition & 3 deletions internal/trie/node/dirty.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ package node
func (n *Node) SetDirty() {
n.Dirty = true
// A node is marked dirty if its key or value is modified.
// This means its cached encoding and hash fields are no longer
// valid. To improve memory usage, we clear these fields.
n.Encoding = nil
// This means its Merkle value field is no longer valid.
n.MerkleValue = nil
}

Expand Down
6 changes: 0 additions & 6 deletions internal/trie/node/dirty_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,12 @@ func Test_Node_SetDirty(t *testing.T) {
}{
"not dirty to dirty": {
node: Node{
Encoding: []byte{1},
MerkleValue: []byte{1},
},
expected: Node{Dirty: true},
},
"dirty to dirty": {
node: Node{
Encoding: []byte{1},
MerkleValue: []byte{1},
Dirty: true,
},
Expand Down Expand Up @@ -54,22 +52,18 @@ func Test_Node_SetClean(t *testing.T) {
}{
"not dirty to not dirty": {
node: Node{
Encoding: []byte{1},
MerkleValue: []byte{1},
},
expected: Node{
Encoding: []byte{1},
MerkleValue: []byte{1},
},
},
"dirty to not dirty": {
node: Node{
Encoding: []byte{1},
MerkleValue: []byte{1},
Dirty: true,
},
expected: Node{
Encoding: []byte{1},
MerkleValue: []byte{1},
},
},
Expand Down
16 changes: 0 additions & 16 deletions internal/trie/node/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,6 @@ import (
// of this package, and specified in the Polkadot spec at
// https://spec.polkadot.network/#sect-state-storage
func (n *Node) Encode(buffer Buffer) (err error) {
if !n.Dirty && n.Encoding != nil {
_, err = buffer.Write(n.Encoding)
if err != nil {
return fmt.Errorf("cannot write stored encoding to buffer: %w", err)
}
return nil
}

err = encodeHeader(n, buffer)
if err != nil {
return fmt.Errorf("cannot encode header: %w", err)
Expand Down Expand Up @@ -66,13 +58,5 @@ func (n *Node) Encode(buffer Buffer) (err error) {
}
}

if kind == Leaf {
// TODO cache this for branches too and update test cases.
// TODO remove this copying since it defeats the purpose of `buffer`
// and the sync.Pool.
n.Encoding = make([]byte, buffer.Len())
copy(n.Encoding, buffer.Bytes())
}

return nil
}
62 changes: 0 additions & 62 deletions internal/trie/node/encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,37 +26,10 @@ func Test_Node_Encode(t *testing.T) {
testCases := map[string]struct {
node *Node
writes []writeCall
bufferLenCall bool
bufferBytesCall bool
expectedEncoding []byte
wrappedErr error
errMessage string
}{
"clean leaf with encoding": {
node: &Node{
Encoding: []byte{1, 2, 3},
},
writes: []writeCall{
{
written: []byte{1, 2, 3},
},
},
expectedEncoding: []byte{1, 2, 3},
},
"write error for clean leaf with encoding": {
node: &Node{
Encoding: []byte{1, 2, 3},
},
writes: []writeCall{
{
written: []byte{1, 2, 3},
err: errTest,
},
},
expectedEncoding: []byte{1, 2, 3},
wrappedErr: errTest,
errMessage: "cannot write stored encoding to buffer: test error",
},
"leaf header encoding error": {
node: &Node{
Key: make([]byte, 1),
Expand Down Expand Up @@ -123,8 +96,6 @@ func Test_Node_Encode(t *testing.T) {
written: []byte{12, 4, 5, 6},
},
},
bufferLenCall: true,
bufferBytesCall: true,
expectedEncoding: []byte{1, 2, 3},
},
"leaf with empty value success": {
Expand All @@ -142,35 +113,8 @@ func Test_Node_Encode(t *testing.T) {
written: []byte{0},
},
},
bufferLenCall: true,
bufferBytesCall: true,
expectedEncoding: []byte{1, 2, 3},
},
"clean branch with encoding": {
node: &Node{
Children: make([]*Node, ChildrenCapacity),
Encoding: []byte{1, 2, 3},
},
writes: []writeCall{
{ // stored encoding
written: []byte{1, 2, 3},
},
},
},
"write error for clean branch with encoding": {
node: &Node{
Children: make([]*Node, ChildrenCapacity),
Encoding: []byte{1, 2, 3},
},
writes: []writeCall{
{ // stored encoding
written: []byte{1, 2, 3},
err: errTest,
},
},
wrappedErr: errTest,
errMessage: "cannot write stored encoding to buffer: test error",
},
"branch header encoding error": {
node: &Node{
Children: make([]*Node, ChildrenCapacity),
Expand Down Expand Up @@ -362,12 +306,6 @@ func Test_Node_Encode(t *testing.T) {
}
previousCall = call
}
if testCase.bufferLenCall {
buffer.EXPECT().Len().Return(len(testCase.expectedEncoding))
}
if testCase.bufferBytesCall {
buffer.EXPECT().Bytes().Return(testCase.expectedEncoding)
}

err := testCase.node.Encode(buffer)

Expand Down
33 changes: 6 additions & 27 deletions internal/trie/node/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,12 @@ func (n *Node) CalculateRootMerkleValue() (merkleValue []byte, err error) {
// and a merkle value writer, such that buffer sync pools can be used
// by the caller.
func (n *Node) EncodeAndHash() (encoding, merkleValue []byte, err error) {
if !n.Dirty && n.Encoding != nil && n.MerkleValue != nil {
return n.Encoding, n.MerkleValue, nil
}

encoding, err = n.encodeIfNeeded()
encodingBuffer := bytes.NewBuffer(nil)
err = n.Encode(encodingBuffer)
if err != nil {
return nil, nil, fmt.Errorf("encoding node: %w", err)
}
encoding = encodingBuffer.Bytes()

const maxMerkleValueSize = 32
merkleValueBuffer := bytes.NewBuffer(make([]byte, 0, maxMerkleValueSize))
Expand All @@ -118,15 +116,12 @@ func (n *Node) EncodeAndHash() (encoding, merkleValue []byte, err error) {
// and a merkle value writer, such that buffer sync pools can be used
// by the caller.
func (n *Node) EncodeAndHashRoot() (encoding, merkleValue []byte, err error) {
const rootMerkleValueLength = 32
if !n.Dirty && n.Encoding != nil && len(n.MerkleValue) == rootMerkleValueLength {
return n.Encoding, n.MerkleValue, nil
}

encoding, err = n.encodeIfNeeded()
encodingBuffer := bytes.NewBuffer(nil)
err = n.Encode(encodingBuffer)
if err != nil {
return nil, nil, fmt.Errorf("encoding node: %w", err)
}
encoding = encodingBuffer.Bytes()

const merkleValueSize = 32
merkleValueBuffer := bytes.NewBuffer(make([]byte, 0, merkleValueSize))
Expand All @@ -139,19 +134,3 @@ func (n *Node) EncodeAndHashRoot() (encoding, merkleValue []byte, err error) {

return encoding, merkleValue, nil
}

func (n *Node) encodeIfNeeded() (encoding []byte, err error) {
if !n.Dirty && n.Encoding != nil {
return n.Encoding, nil // no need to copy
}

buffer := bytes.NewBuffer(nil)
err = n.Encode(buffer)
if err != nil {
return nil, fmt.Errorf("encoding: %w", err)
}

n.Encoding = buffer.Bytes()

return n.Encoding, nil // no need to copy
}
Loading

0 comments on commit 856780b

Please sign in to comment.