-
Notifications
You must be signed in to change notification settings - Fork 272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Fast storage optimization for queries and iterations #468
Merged
tac0turtle
merged 123 commits into
cosmos:master
from
osmosis-labs:roman/iavl_data_locality
Apr 9, 2022
Merged
Changes from all commits
Commits
Show all changes
123 commits
Select commit
Hold shift + click to select a range
0d91ad8
Add unbounded key string to KeyFormat
ValarDragon 4040da1
Add test vectors for unbounded length keys
ValarDragon 3740d0b
Add some notes
ValarDragon e175568
update .gitignore
jtieri 24cb701
Add FastNode struct
jtieri 1c0188d
WIP: make Get work with new FastNode
jtieri 67f000a
when retrieving fastnode fails, return errors vs. panic
jtieri ab5f2cd
add comments clarifying what index represents
jtieri 3618100
make the linter happy
jtieri 44c6035
Add small tweaks to fast_node.go
jtieri 19fe40d
add TODO & small linter tweaks
jtieri fdfe996
Update comment
ValarDragon 8b5b9a7
update fast node cache in set
p0mvn db293f9
add debugging output when falling back to original logic
jtieri 606b56f
return error instead of panic
jtieri 36f0760
WIP: refactor set ops to work with fast store
jtieri 36b30a1
update Set of mutable tree, begin unit testing
p0mvn 2a5c19b
update GetVersioned to check fast nodes before trying the immutable
p0mvn 5e942a1
check fast node version before nil value check in get of immutable tree
p0mvn 2dcf439
fix small bugs and typos, continue writing unit tests for Set
p0mvn 1dfc795
unit test saveFastNodeVersion
p0mvn 2e4daa7
simplify storing unsaved fast nodes
p0mvn 6f4630c
resolve a bug with not writing prefix for fast node to disk
p0mvn 599937d
remove fast nodes from disk on save and clear fast cache when version…
p0mvn 348d7be
resolve an issue with randomized tests caused by the fast node cache …
p0mvn ec07dd1
split unsaved fast node changes into additions and removals
p0mvn ffb4c30
save fast node removals
p0mvn 6a1d2d7
move fast node cache clearing to nodedb
p0mvn 1db02b3
use regular logic only when fast node version is greater than immutab…
p0mvn f3dcb71
clean up tree_random_test.go
p0mvn 0a765bb
clear unsaved fast node removals on rollback
p0mvn b8a9b0a
fix randomized test failures caused by a typo in ndb DeleteVersion fo…
p0mvn 28da34c
implement GetFast method to preserve Get with correct index return, c…
p0mvn 0a0ba33
ensure Get and GetFast return the same values in tree_random_test.go
p0mvn 5bf8c81
test fast node cache is live in random tree tests
p0mvn 41c1827
improve mutable tree unit tests related to new functionality
p0mvn 5fb6d2b
clean up tree_test.go
p0mvn c8def88
implement GetVersionedFast to preserve the index in GetVersioned
p0mvn eca5e6c
restore accidentally deleted test in mutable tree test
p0mvn 7a5c131
spurious whitespace
p0mvn f9ab00e
refactor mutable tree
p0mvn 2f54c55
fix comment in mutable tree
p0mvn ff5a3ed
add benchmark results
p0mvn 754e8e8
avoid redundant full tree search in GetFast of immutable tree when fa…
p0mvn f8c085b
fix naming for bench test get on random keys
p0mvn 4064369
use method for get latestversio in get fast
p0mvn 9abde82
optimize GetFast, perform a refactor to ensure that fast nodes on dis…
p0mvn 35cacc4
add latest bench
p0mvn 0e09c6b
Fast Node Iteration (#7)
p0mvn 51b1f33
refactor iterate methods of mutable and immutable trees
p0mvn e773a16
resolve some warnings
p0mvn 4120794
remove old bench results
p0mvn dfd175e
refactor bench tests for iteration
p0mvn c6d015d
Fast Cache Migration (#9)
p0mvn 4d9facc
Rename Get to GetWithIndex and GetFast to Get
p0mvn f2c529c
refactor and clean up bench tests
p0mvn 9b532aa
remove extra byte from fast node length
p0mvn dc316b9
clean up immutable tree
p0mvn b04993d
refactor nil tree or ndb error for the iterators and their tests
p0mvn d4b6e01
avoid exporting methods for getting unsaved additions and removals
p0mvn 649387d
refactor fast upgrade to read from tree instead of traversing disk no…
p0mvn ca9904b
remove unneeded comment
p0mvn 9f62bfb
refer to storage version consistently across the project
p0mvn 3ca6772
fix more warnings
p0mvn 2fb41e8
optimize removal of fast nodes from cache on deletion
p0mvn 521d596
small changes in teh mutable tree
p0mvn 176993d
correct storage version key
p0mvn 4bb3ecb
auto set fast version in SaveVersion
p0mvn 19d5195
avoid exporting new methods of the immutable tree
p0mvn eb8ae54
go fmt
p0mvn 7678a17
Fix comment in fast iterator
p0mvn d98284a
add extra comment for domain in fast iterator
p0mvn c5bf2e5
add comments for moving the iterator before the first element
p0mvn d1e999a
add comment for describing what mirror is in assertIterator of testutils
p0mvn 69f84a8
fix checking the mirror for descending iterator in tests
p0mvn 5a67677
Update testutils_test.go with a comment
p0mvn c925b01
Update benchmarks/bench_test.go with a comment for runKnownQueriesFast
p0mvn 313615e
Update benchmarks/bench_test.go with a comment for runQueriesFast
p0mvn 08cde5a
export IsFastCacheEnabled and add an assert in bench tests
p0mvn b5f17cc
Update comment immutable_tree.go
p0mvn accf09d
Update comment for migration in mutable_tree.go
p0mvn ac7bc8b
simlify Iterate in mutable tree, add debug log for
p0mvn cbfd88a
Fast Cache - Downgrade - reupgrade protection and other improvements …
p0mvn b0ea918
address comments from unsaved fast iterator PR
p0mvn 45e1ed4
expose isUpgradeable method on mutable tree and unit test (#17)
p0mvn 611e18c
resolve problems with rebasing
p0mvn 73cc200
update CHANGELOG.md
p0mvn ff8d21c
tendermint v0.35.0
p0mvn 1e62146
fix String() bench
p0mvn d27a0c3
fix duplication lint in iterator_test.go
p0mvn 7009a9c
fix lint for tree.ndb.DeleteFastNode error return not checked
p0mvn e4ed678
fix Error return value of `ndb.resetBatch` is not checked
p0mvn 34d4070
fix Error return value of `ndb.traversePrefix` is not checked
p0mvn 3e064e0
fix Error: struct of size 64 bytes could be of size 56 bytes
p0mvn 3433ef1
fix Error: `comitted` is a misspelling of `committed`
p0mvn 3783679
fix issues in basic_test.go
p0mvn 6bc3fe6
address comments in fast_iterator.go
p0mvn 4fe1d9f
address comments in immutable tree
p0mvn 484e5a1
address comments in iterator.go
p0mvn 81ac12e
address comments in key_format.go
p0mvn 57dbc38
address remaining comments
p0mvn b016408
fix Error: Error return value of `ndb.batch.Write` is not checked
p0mvn eefb4e7
fix Error: receiver name t should be consistent with previous receive…
p0mvn 80b2309
fix Error: struct of size 48 bytes could be of size 40 bytes
p0mvn e47a345
go fmt
p0mvn a124043
more linter fixes
p0mvn eebf868
fix remaining linter problems
p0mvn 34555e3
upgrade tm-db and comment out broken bencher databases
p0mvn 141d98d
skip iterations for BenchmarkLevelDBLargeData bench
p0mvn 543206b
attempt to fix linter
p0mvn e44a79a
force GC, no cache during migration, auto heap profile (#19)
p0mvn 1236c4c
sync access to fast node cache to avoid concurrent write fatal error …
p0mvn 98d02eb
update go.mod and go.sum to match master versions
p0mvn 2feedb7
merge master into roman/iavl_data_locality
p0mvn b5fd3db
Merge branch 'master' into roman/iavl_data_locality
p0mvn 67b66c0
revert #23 (sync access to fast node cache), fix bug related to old h…
p0mvn 69129bf
fix concurrent map panic when querying and comittting concurrently
p0mvn b4f8932
avoid clearing fast node cache during pruning (#35)
p0mvn 71e959a
fix data race related to VersionExists (#36)
p0mvn b0d8cdf
hardcode fast node cache size to 100k
p0mvn f660a25
go fmt
p0mvn f56de3a
restore proof_ics23.go
p0mvn ff9f32d
fix linter
p0mvn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,3 +12,6 @@ cpu*.out | |
mem*.out | ||
cpu*.pdf | ||
mem*.pdf | ||
|
||
# IDE files | ||
.idea/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,6 @@ func randBytes(length int) []byte { | |
key := make([]byte, length) | ||
// math.rand.Read always returns err=nil | ||
// we do not need cryptographic randomness for this test: | ||
//nolint:gosec | ||
rand.Read(key) | ||
return key | ||
} | ||
|
@@ -57,21 +56,95 @@ func commitTree(b *testing.B, t *iavl.MutableTree) { | |
} | ||
} | ||
|
||
func runQueries(b *testing.B, t *iavl.MutableTree, keyLen int) { | ||
// 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) | ||
} | ||
} | ||
|
||
func runKnownQueries(b *testing.B, t *iavl.MutableTree, keys [][]byte) { | ||
// 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)] | ||
t.Get(q) | ||
} | ||
} | ||
|
||
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) | ||
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)] | ||
index, value := itree.GetWithIndex(q) | ||
require.True(b, index >= 0, "the index must not be negative") | ||
require.NotNil(b, value, "the value should exist") | ||
} | ||
} | ||
|
||
func runIterationFast(b *testing.B, t *iavl.MutableTree, expectedSize int) { | ||
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) | ||
require.Nil(b, itr.Close(), ".Close should not error out") | ||
} | ||
} | ||
|
||
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) // create slow iterator directly | ||
iterate(b, itr, expectedSize) | ||
require.Nil(b, itr.Close(), ".Close should not error out") | ||
} | ||
} | ||
|
||
func iterate(b *testing.B, itr db.Iterator, expectedSize int) { | ||
b.StartTimer() | ||
keyValuePairs := make([][][]byte, 0, expectedSize) | ||
for i := 0; i < expectedSize && itr.Valid(); i++ { | ||
itr.Next() | ||
keyValuePairs = append(keyValuePairs, [][]byte{itr.Key(), itr.Value()}) | ||
} | ||
b.StopTimer() | ||
if g, w := len(keyValuePairs), expectedSize; g != w { | ||
b.Errorf("iteration count mismatch: got=%d, want=%d", g, w) | ||
} else { | ||
b.Logf("completed %d iterations", len(keyValuePairs)) | ||
} | ||
} | ||
|
||
// func runInsert(b *testing.B, t *iavl.MutableTree, keyLen, dataLen, blockSize int) *iavl.MutableTree { | ||
// for i := 1; i <= b.N; i++ { | ||
// t.Set(randBytes(keyLen), randBytes(dataLen)) | ||
|
@@ -132,7 +205,7 @@ func runBlock(b *testing.B, t *iavl.MutableTree, keyLen, dataLen, blockSize int, | |
data := randBytes(dataLen) | ||
|
||
// perform query and write on check and then real | ||
// check.Get(key) | ||
// check.GetFast(key) | ||
// check.Set(key, data) | ||
real.Get(key) | ||
real.Set(key, data) | ||
|
@@ -175,11 +248,11 @@ func BenchmarkMedium(b *testing.B) { | |
benchmarks := []benchmark{ | ||
{"memdb", 100000, 100, 16, 40}, | ||
{"goleveldb", 100000, 100, 16, 40}, | ||
{"cleveldb", 100000, 100, 16, 40}, | ||
// {"cleveldb", 100000, 100, 16, 40}, | ||
// FIXME: idk why boltdb is too slow !? | ||
// {"boltdb", 100000, 100, 16, 40}, | ||
{"rocksdb", 100000, 100, 16, 40}, | ||
{"badgerdb", 100000, 100, 16, 40}, | ||
// {"rocksdb", 100000, 100, 16, 40}, | ||
// {"badgerdb", 100000, 100, 16, 40}, | ||
} | ||
runBenchmarks(b, benchmarks) | ||
} | ||
|
@@ -188,10 +261,10 @@ func BenchmarkSmall(b *testing.B) { | |
benchmarks := []benchmark{ | ||
{"memdb", 1000, 100, 4, 10}, | ||
{"goleveldb", 1000, 100, 4, 10}, | ||
{"cleveldb", 1000, 100, 4, 10}, | ||
{"boltdb", 1000, 100, 4, 10}, | ||
{"rocksdb", 1000, 100, 4, 10}, | ||
{"badgerdb", 1000, 100, 4, 10}, | ||
// {"cleveldb", 1000, 100, 4, 10}, | ||
// {"boltdb", 1000, 100, 4, 10}, | ||
// {"rocksdb", 1000, 100, 4, 10}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could you enable rocksdb benchmarks as well? |
||
// {"badgerdb", 1000, 100, 4, 10}, | ||
} | ||
runBenchmarks(b, benchmarks) | ||
} | ||
|
@@ -202,8 +275,8 @@ func BenchmarkLarge(b *testing.B) { | |
{"goleveldb", 1000000, 100, 16, 40}, | ||
// FIXME: idk why boltdb is too slow !? | ||
// {"boltdb", 1000000, 100, 16, 40}, | ||
{"rocksdb", 1000000, 100, 16, 40}, | ||
{"badgerdb", 1000000, 100, 16, 40}, | ||
// {"rocksdb", 1000000, 100, 16, 40}, | ||
// {"badgerdb", 1000000, 100, 16, 40}, | ||
} | ||
runBenchmarks(b, benchmarks) | ||
} | ||
|
@@ -287,14 +360,38 @@ func runSuite(b *testing.B, d db.DB, initSize, blockSize, keyLen, dataLen int) { | |
|
||
b.ResetTimer() | ||
|
||
b.Run("query-miss", func(sub *testing.B) { | ||
b.Run("query-no-in-tree-guarantee-fast", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
runQueriesFast(sub, t, keyLen) | ||
}) | ||
b.Run("query-no-in-tree-guarantee-slow", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
runQueriesSlow(sub, t, keyLen) | ||
}) | ||
// | ||
b.Run("query-hits-fast", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
runQueries(sub, t, keyLen) | ||
runKnownQueriesFast(sub, t, keys) | ||
}) | ||
b.Run("query-hits", func(sub *testing.B) { | ||
b.Run("query-hits-slow", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
runKnownQueries(sub, t, keys) | ||
runKnownQueriesSlow(sub, t, keys) | ||
}) | ||
// | ||
// Iterations for BenchmarkLevelDBLargeData timeout bencher in CI so | ||
// we must skip them. | ||
if b.Name() != "BenchmarkLevelDBLargeData" { | ||
b.Run("iteration-fast", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
runIterationFast(sub, t, initSize) | ||
}) | ||
b.Run("iteration-slow", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
runIterationSlow(sub, t, initSize) | ||
}) | ||
} | ||
|
||
// | ||
b.Run("update", func(sub *testing.B) { | ||
sub.ReportAllocs() | ||
t = runUpdate(sub, t, dataLen, blockSize, keys) | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please remove the 3 lines above. We are targetting master, so #468 should go under Unreleased