Skip to content

Commit

Permalink
Fast Cache - Downgrade - reupgrade protection and other improvements (#…
Browse files Browse the repository at this point in the history
…12)

* add leaf hash to fast node and unit test

* refactor get with index and get by index, fix migration in load version and lazy load version

* use Get in GetVersioned of mutable tree

* refactor non membership proof to use fast storage if available

* bench non-membership proof

* fix bench tests to work with the new changes

* add downgrade-reupgrade protection and unit test

* remove leaf hash from fast node

* resolve multithreading bug related to iterators not being closed

* clean up

* use correct tree in bench tests

* add cache to tree used to bench non membership proofs

* add benc tests for GetWithIndex and GetByIndex

* revert GetWithIndex and GetByIndex

* remove unused import

* unit test re-upgrade protection and fix small issues

* remove redundant setStorageVersion method

* fix bug with appending to live stage version to storage version and nit test

* add comment for setFastStorageVersionToBatch

* refactor and improve unit tests for reupgrade protection

* rename ndb's isFastStorageEnabled to hasUpgradedToFastStorage and add comments

* comment out new implementation for GetNonMembershipProof

* update comments in nodedb to reflect the difference between hasUpgradedToFastStorage and shouldForceFastStorageUpdate

* refactor nodedb tests

* downgrade tendermint to 0.34.14 - osmosis's latest cosmos sdk does not support 0.35.0

* fix bug where fast storage was not enabled when version 0 was attempted to be loaded

* implement unsaved fast iterator to be used in mutable tree (#16)
  • Loading branch information
p0mvn committed Feb 11, 2022
1 parent e309f7f commit 2dbde70
Show file tree
Hide file tree
Showing 19 changed files with 1,300 additions and 976 deletions.
31 changes: 27 additions & 4 deletions benchmarks/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func commitTree(b *testing.B, t *iavl.MutableTree) {

// queries random keys against live state. Keys are almost certainly not in the tree.
func runQueriesFast(b *testing.B, t *iavl.MutableTree, keyLen int) {
require.True(b, t.IsFastCacheEnabled())
for i := 0; i < b.N; i++ {
q := randBytes(keyLen)
t.Get(q)
Expand All @@ -67,6 +68,7 @@ func runQueriesFast(b *testing.B, t *iavl.MutableTree, keyLen int) {

// queries keys that are known to be in state
func runKnownQueriesFast(b *testing.B, t *iavl.MutableTree, keys [][]byte) {
require.True(b, t.IsFastCacheEnabled()) // to ensure fast storage is enabled
l := int32(len(keys))
for i := 0; i < b.N; i++ {
q := keys[rand.Int31n(l)]
Expand All @@ -75,22 +77,43 @@ func runKnownQueriesFast(b *testing.B, t *iavl.MutableTree, keys [][]byte) {
}

func runQueriesSlow(b *testing.B, t *iavl.MutableTree, keyLen int) {
b.StopTimer()
// Save version to get an old immutable tree to query against,
// Fast storage is not enabled on old tree versions, allowing us to bench the desired behavior.
_, version, err := t.SaveVersion()
require.NoError(b, err)

itree, err := t.GetImmutable(version - 1)
require.NoError(b, err)
require.False(b, itree.IsFastCacheEnabled()) // to ensure fast storage is not enabled

b.StartTimer()
for i := 0; i < b.N; i++ {
q := randBytes(keyLen)
t.GetWithIndex(q)
itree.GetWithIndex(q)
}
}

func runKnownQueriesSlow(b *testing.B, t *iavl.MutableTree, keys [][]byte) {
b.StopTimer()
// Save version to get an old immutable tree to query against,
// Fast storage is not enabled on old tree versions, allowing us to bench the desired behavior.
_, version, err := t.SaveVersion()
require.NoError(b, err)

itree, err := t.GetImmutable(version - 1)
require.NoError(b, err)
require.False(b, itree.IsFastCacheEnabled()) // to ensure fast storage is not enabled
b.StartTimer()
l := int32(len(keys))
for i := 0; i < b.N; i++ {
q := keys[rand.Int31n(l)]
t.GetWithIndex(q)
itree.GetWithIndex(q)
}
}

func runIterationFast(b *testing.B, t *iavl.MutableTree, expectedSize int) {
require.True(b, t.IsFastCacheEnabled()) // to ensure that fast iterator is returned.
require.True(b, t.IsFastCacheEnabled()) // to ensure fast storage is enabled
for i := 0; i < b.N; i++ {
itr := t.ImmutableTree.Iterator(nil, nil, false)
iterate(b, itr, expectedSize)
Expand All @@ -100,7 +123,7 @@ func runIterationFast(b *testing.B, t *iavl.MutableTree, expectedSize int) {

func runIterationSlow(b *testing.B, t *iavl.MutableTree, expectedSize int) {
for i := 0; i < b.N; i++ {
itr := iavl.NewIterator(nil, nil, false, t.ImmutableTree)
itr := iavl.NewIterator(nil, nil, false, t.ImmutableTree) // create slow iterator directly
iterate(b, itr, expectedSize)
itr.Close()
}
Expand Down
4 changes: 2 additions & 2 deletions export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ func setupExportTreeRandom(t *testing.T) *ImmutableTree {
keySize = 16
valueSize = 16

versions = 32 // number of versions to generate
versionOps = 4096 // number of operations (create/update/delete) per version
versions = 8 // number of versions to generate
versionOps = 1024 // number of operations (create/update/delete) per version
updateRatio = 0.4 // ratio of updates out of all operations
deleteRatio = 0.2 // ratio of deletes out of all operations
)
Expand Down
5 changes: 2 additions & 3 deletions fast_iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,11 @@ func (iter *FastIterator) Next() {

// Close implements dbm.Iterator
func (iter *FastIterator) Close() error {
iter.fastIterator = nil
iter.valid = false

if iter.fastIterator != nil {
iter.err = iter.fastIterator.Close()
}
iter.valid = false
iter.fastIterator = nil
return iter.err
}

Expand Down
3 changes: 1 addition & 2 deletions fast_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ type FastNode struct {
key []byte
versionLastUpdatedAt int64
value []byte
// leafHash []byte // TODO: Look into if this would help with proof stuff.
}

// NewFastNode returns a new fast node from a value and version.
Expand All @@ -34,7 +33,7 @@ func DeserializeFastNode(key []byte, buf []byte) (*FastNode, error) {

val, _, cause := decodeBytes(buf)
if cause != nil {
return nil, errors.Wrap(cause, "decoding node.value")
return nil, errors.Wrap(cause, "decoding fastnode.value")
}

fastNode := &FastNode{
Expand Down
58 changes: 58 additions & 0 deletions fast_node_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package iavl

import (
"bytes"
"encoding/hex"
"testing"

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

func TestFastNode_encodedSize(t *testing.T) {
fastNode := &FastNode{
key: randBytes(10),
versionLastUpdatedAt: 1,
value: randBytes(20),
}

expectedSize := 1 + len(fastNode.value) + 1

require.Equal(t, expectedSize, fastNode.encodedSize())
}

func TestFastNode_encode_decode(t *testing.T) {
testcases := map[string]struct {
node *FastNode
expectHex string
expectError bool
}{
"nil": {nil, "", true},
"empty": {&FastNode{}, "0000", false},
"inner": {&FastNode{
key: []byte{0x4},
versionLastUpdatedAt: 1,
value: []byte{0x2},
}, "020102", false},
}
for name, tc := range testcases {
tc := tc
t.Run(name, func(t *testing.T) {
var buf bytes.Buffer
err := tc.node.writeBytes(&buf)
if tc.expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tc.expectHex, hex.EncodeToString(buf.Bytes()))

node, err := DeserializeFastNode(tc.node.key, buf.Bytes())
require.NoError(t, err)
// since value and leafHash are always decoded to []byte{} we augment the expected struct here
if tc.node.value == nil {
tc.node.value = []byte{}
}
require.Equal(t, tc.node, node)
})
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.7.0
github.com/tendermint/tendermint v0.35.0
github.com/tendermint/tendermint v0.34.14
github.com/tendermint/tm-db v0.6.4
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4
Expand Down
Loading

0 comments on commit 2dbde70

Please sign in to comment.