From bb7cd9ce5366b48f801cd54c27fc61fe4ef7db12 Mon Sep 17 00:00:00 2001 From: Erik Grinaker Date: Wed, 24 Jun 2020 11:28:24 +0200 Subject: [PATCH] remove pruning (#274) --- Makefile | 5 - benchmarks/bench_test.go | 74 +- benchmarks/prune_test.go | 98 --- .../Adityas-MBP-pruning-results.txt | 9 - .../158-pruning/Adityas-MBP-results.txt | 147 ---- .../DigitalOcean-4vcpu-8gb-160gb.txt | 9 - .../158-pruning/sdk-pruning-1-0-results.txt | 164 ----- .../158-pruning/sdk-pruning-10-2-results.txt | 163 ----- .../158-pruning/sdk-pruning-100-2-results.txt | 163 ----- .../sdk-pruning-1000-5-results.txt | 162 ----- .../158-pruning/sdk-pruning-5-2-results.txt | 163 ----- doc.go | 2 +- docs/architecture/adr-001-flush-version.md | 140 ---- docs/node/node.md | 7 - docs/node/nodedb.md | 44 +- docs/overview.md | 1 - docs/tree/PRUNING.md | 41 -- docs/tree/mutable_tree.md | 3 - export_test.go | 41 -- go.mod | 1 - immutable_tree.go | 13 +- import.go | 4 +- metadata.go | 18 - metadata_test.go | 26 - mutable_tree.go | 215 +----- mutable_tree_test.go | 198 ------ node.go | 3 +- nodedb.go | 653 +++--------------- nodedb_test.go | 44 -- options.go | 32 +- proto/iavl/types.proto | 18 - pruning_test.go | 532 -------------- testutils_test.go | 10 +- tree_random_test.go | 6 +- tree_test.go | 4 +- types.pb.go | 361 +--------- 36 files changed, 179 insertions(+), 3395 deletions(-) delete mode 100644 benchmarks/prune_test.go delete mode 100644 benchmarks/results/158-pruning/Adityas-MBP-pruning-results.txt delete mode 100644 benchmarks/results/158-pruning/Adityas-MBP-results.txt delete mode 100644 benchmarks/results/158-pruning/DigitalOcean-4vcpu-8gb-160gb.txt delete mode 100644 benchmarks/results/158-pruning/sdk-pruning-1-0-results.txt delete mode 100644 benchmarks/results/158-pruning/sdk-pruning-10-2-results.txt delete mode 100644 benchmarks/results/158-pruning/sdk-pruning-100-2-results.txt delete mode 100644 benchmarks/results/158-pruning/sdk-pruning-1000-5-results.txt delete mode 100644 benchmarks/results/158-pruning/sdk-pruning-5-2-results.txt delete mode 100644 docs/architecture/adr-001-flush-version.md delete mode 100644 docs/tree/PRUNING.md delete mode 100644 metadata.go delete mode 100644 metadata_test.go delete mode 100644 pruning_test.go diff --git a/Makefile b/Makefile index 5d124fdee..e2940e945 100644 --- a/Makefile +++ b/Makefile @@ -60,11 +60,6 @@ fullbench: go test $(LDFLAGS) -timeout=60m -bench=LevelDB . .PHONY: fullbench -benchprune: - cd benchmarks && \ - go test -bench=PruningStrategies -timeout=24h -.PHONY: benchprune - # note that this just profiles the in-memory version, not persistence profile: cd benchmarks && \ diff --git a/benchmarks/bench_test.go b/benchmarks/bench_test.go index 6b77d95ea..eaa2cfdec 100644 --- a/benchmarks/bench_test.go +++ b/benchmarks/bench_test.go @@ -13,6 +13,8 @@ import ( db "github.com/tendermint/tm-db" ) +const historySize = 20 + func randBytes(length int) []byte { key := make([]byte, length) // math.rand.Read always returns err=nil @@ -22,8 +24,8 @@ func randBytes(length int) []byte { return key } -func prepareTree(b *testing.B, snapdb db.DB, memdb db.DB, keepEvery int64, keepRecent int64, size, keyLen, dataLen int) (*iavl.MutableTree, [][]byte) { - t, err := iavl.NewMutableTreeWithOpts(snapdb, memdb, size, iavl.PruningOptions(keepEvery, keepRecent)) +func prepareTree(b *testing.B, db db.DB, size, keyLen, dataLen int) (*iavl.MutableTree, [][]byte) { + t, err := iavl.NewMutableTreeWithOpts(db, size, nil) require.NoError(b, err) keys := make([][]byte, size) @@ -37,15 +39,22 @@ func prepareTree(b *testing.B, snapdb db.DB, memdb db.DB, keepEvery int64, keepR return t, keys } -// commit tree saves a new version according to pruning strategy passed into IAVL +// commit tree saves a new version and deletes old ones according to historySize func commitTree(b *testing.B, t *iavl.MutableTree) { t.Hash() - _, _, err := t.SaveVersion() //this will flush for us every so often + _, version, err := t.SaveVersion() if err != nil { b.Errorf("Can't save: %v", err) } + + if version > historySize { + err = t.DeleteVersion(version - historySize) + if err != nil { + b.Errorf("Can't delete: %v", err) + } + } } func runQueries(b *testing.B, t *iavl.MutableTree, keyLen int) { @@ -217,40 +226,29 @@ func BenchmarkLevelDBLargeData(b *testing.B) { func runBenchmarks(b *testing.B, benchmarks []benchmark) { fmt.Printf("%s\n", iavl.GetVersionInfo()) - pruningStrategies := []pruningstrat{ - {1, 0}, // default pruning strategy - {0, 1}, // keep single recent version - {100, 5}, // simple pruning - {1000, 10}, // average pruning - {1000, 1}, // extreme pruning - {10000, 100}, // SDK pruning - } - for _, ps := range pruningStrategies { - ps := ps - for _, bb := range benchmarks { - bb := bb - prefix := fmt.Sprintf("%s-%d-%d-%d-%d-%d-%d", bb.dbType, ps.keepEvery, ps.keepRecent, - bb.initSize, bb.blockSize, bb.keyLen, bb.dataLen) - - // prepare a dir for the db and cleanup afterwards - dirName := fmt.Sprintf("./%s-db", prefix) - defer func() { - err := os.RemoveAll(dirName) - if err != nil { - b.Errorf("%+v\n", err) - } - }() - - // note that "" leads to nil backing db! - var d db.DB - if bb.dbType != "nodb" { - d = db.NewDB("test", bb.dbType, dirName) - defer d.Close() + for _, bb := range benchmarks { + bb := bb + prefix := fmt.Sprintf("%s-%d-%d-%d-%d", bb.dbType, + bb.initSize, bb.blockSize, bb.keyLen, bb.dataLen) + + // prepare a dir for the db and cleanup afterwards + dirName := fmt.Sprintf("./%s-db", prefix) + defer func() { + err := os.RemoveAll(dirName) + if err != nil { + b.Errorf("%+v\n", err) } - b.Run(prefix, func(sub *testing.B) { - runSuite(sub, d, ps.keepEvery, ps.keepRecent, bb.initSize, bb.blockSize, bb.keyLen, bb.dataLen) - }) + }() + + // note that "" leads to nil backing db! + var d db.DB + if bb.dbType != "nodb" { + d = db.NewDB("test", bb.dbType, dirName) + defer d.Close() } + b.Run(prefix, func(sub *testing.B) { + runSuite(sub, d, bb.initSize, bb.blockSize, bb.keyLen, bb.dataLen) + }) } } @@ -263,12 +261,12 @@ func memUseMB() float64 { return mb } -func runSuite(b *testing.B, d db.DB, keepEvery int64, keepRecent int64, initSize, blockSize, keyLen, dataLen int) { +func runSuite(b *testing.B, d db.DB, initSize, blockSize, keyLen, dataLen int) { // measure mem usage runtime.GC() init := memUseMB() - t, keys := prepareTree(b, d, db.NewMemDB(), keepEvery, keepRecent, initSize, keyLen, dataLen) + t, keys := prepareTree(b, d, initSize, keyLen, dataLen) used := memUseMB() - init fmt.Printf("Init Tree took %0.2f MB\n", used) diff --git a/benchmarks/prune_test.go b/benchmarks/prune_test.go deleted file mode 100644 index dfc295ba3..000000000 --- a/benchmarks/prune_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package benchmarks - -import ( - "fmt" - "math/rand" - "os" - "runtime" - "testing" - - db "github.com/tendermint/tm-db" -) - -type pruningstrat struct { - keepEvery, keepRecent int64 -} - -// To test effect of pruning strategy, we must measure time to execute many blocks -// Execute 30000 blocks with the given IAVL tree's pruning strategy -func runBlockChain(b *testing.B, prefix string, keepEvery int64, keepRecent int64, keyLen, dataLen int) { - // prepare a dir for the db and cleanup afterwards - dirName := fmt.Sprintf("./%s-db", prefix) - defer func() { - err := os.RemoveAll(dirName) - if err != nil { - b.Errorf("%+v\n", err) - } - }() - - runtime.GC() - - // always initialize tree with goleveldb as snapshotDB and memDB as recentDB - snapDB := db.NewDB("test", "goleveldb", dirName) - defer snapDB.Close() - - var mem runtime.MemStats - runtime.ReadMemStats(&mem) - memSize := mem.Alloc - //maxVersion := 0 - var keys [][]byte - for i := 0; i < 100; i++ { - keys = append(keys, randBytes(keyLen)) - } - - // reset timer after initialization logic - b.ResetTimer() - t, _ := prepareTree(b, snapDB, db.NewMemDB(), keepEvery, keepRecent, 5, keyLen, dataLen) - - // create 30000 versions - for i := 0; i < 5000; i++ { - // set 5 keys per version - for j := 0; j < 5; j++ { - index := rand.Int63n(100) - t.Set(keys[index], randBytes(dataLen)) - } - _, _, err := t.SaveVersion() - if err != nil { - b.Errorf("Can't save version %d: %v", i, err) - } - - // Pause timer to garbage-collect and remeasure memory usage - b.StopTimer() - runtime.GC() - runtime.ReadMemStats(&mem) - // update memSize if it has increased after saveVersion - if memSize < mem.Alloc { - memSize = mem.Alloc - //maxVersion = i - } - b.StartTimer() - } - - //fmt.Printf("Maxmimum Memory usage was %0.2f MB at height %d\n", float64(memSize)/1000000, maxVersion) - b.StopTimer() -} - -func BenchmarkPruningStrategies(b *testing.B) { - ps := []pruningstrat{ - {1, 0}, // default pruning strategy - {1, 1}, - {0, 1}, // keep single recent version - {100, 1}, - {100, 5}, // simple pruning - {5, 1}, - {5, 2}, - {10, 2}, - {1000, 10}, // average pruning - {1000, 1}, // extreme pruning - {10000, 100}, // SDK pruning - } - for _, ps := range ps { - ps := ps - prefix := fmt.Sprintf("PruningStrategy{%d-%d}-KeyLen:%d-DataLen:%d", ps.keepEvery, ps.keepRecent, 16, 40) - - b.Run(prefix, func(sub *testing.B) { - runBlockChain(sub, prefix, ps.keepEvery, ps.keepRecent, 16, 40) - }) - } -} diff --git a/benchmarks/results/158-pruning/Adityas-MBP-pruning-results.txt b/benchmarks/results/158-pruning/Adityas-MBP-pruning-results.txt deleted file mode 100644 index f601ba7a9..000000000 --- a/benchmarks/results/158-pruning/Adityas-MBP-pruning-results.txt +++ /dev/null @@ -1,9 +0,0 @@ -goos: darwin -goarch: amd64 -pkg: github.com/tendermint/iavl/benchmarks -BenchmarkPruningStrategies/PruningStrategy{1-0}-KeyLen:16-DataLen:40-8 1 2837806322 ns/op -BenchmarkPruningStrategies/PruningStrategy{0-1}-KeyLen:16-DataLen:40-8 1 1124373981 ns/op -BenchmarkPruningStrategies/PruningStrategy{100-1}-KeyLen:16-DataLen:40-8 1 1255040658 ns/op -BenchmarkPruningStrategies/PruningStrategy{100-5}-KeyLen:16-DataLen:40-8 1 1459752743 ns/op -PASS -ok github.com/tendermint/iavl/benchmarks 12.375s diff --git a/benchmarks/results/158-pruning/Adityas-MBP-results.txt b/benchmarks/results/158-pruning/Adityas-MBP-results.txt deleted file mode 100644 index 341c236d1..000000000 --- a/benchmarks/results/158-pruning/Adityas-MBP-results.txt +++ /dev/null @@ -1,147 +0,0 @@ -cd benchmarks && \ - go test -bench=RandomBytes . && \ - go test -bench=Small . && \ - go test -bench=Medium . && \ - go test -bench=BenchmarkMemKeySizes . -goos: darwin -goarch: amd64 -pkg: github.com/tendermint/iavl/benchmarks -BenchmarkRandomBytes/random-4-8 30000000 50.1 ns/op -BenchmarkRandomBytes/random-16-8 20000000 77.4 ns/op -BenchmarkRandomBytes/random-32-8 20000000 110 ns/op -BenchmarkRandomBytes/random-100-8 5000000 261 ns/op -BenchmarkRandomBytes/random-1000-8 1000000 2125 ns/op -PASS -ok github.com/tendermint/iavl/benchmarks 9.239s -Init Tree took 0.55 MB -goos: darwin -goarch: amd64 -pkg: github.com/tendermint/iavl/benchmarks -BenchmarkSmall/memdb-1-0-1000-100-4-10/query-miss-8 1000000 2531 ns/op -BenchmarkSmall/memdb-1-0-1000-100-4-10/query-hits-8 500000 3429 ns/op -BenchmarkSmall/memdb-1-0-1000-100-4-10/update-8 10000 116557 ns/op -BenchmarkSmall/memdb-1-0-1000-100-4-10/block-8 100 33767094 ns/op -Init Tree took 0.14 MB -BenchmarkSmall/goleveldb-1-0-1000-100-4-10/query-miss-8 500000 3982 ns/op -BenchmarkSmall/goleveldb-1-0-1000-100-4-10/query-hits-8 300000 5113 ns/op -BenchmarkSmall/goleveldb-1-0-1000-100-4-10/update-8 20000 83604 ns/op -BenchmarkSmall/goleveldb-1-0-1000-100-4-10/block-8 100 14777165 ns/op -Init Tree took 0.58 MB -BenchmarkSmall/memdb-0-1-1000-100-4-10/query-miss-8 500000 2367 ns/op -BenchmarkSmall/memdb-0-1-1000-100-4-10/query-hits-8 500000 2808 ns/op -BenchmarkSmall/memdb-0-1-1000-100-4-10/update-8 50000 33475 ns/op -BenchmarkSmall/memdb-0-1-1000-100-4-10/block-8 300 15409232 ns/op -Init Tree took 0.55 MB -BenchmarkSmall/goleveldb-0-1-1000-100-4-10/query-miss-8 500000 2608 ns/op -BenchmarkSmall/goleveldb-0-1-1000-100-4-10/query-hits-8 500000 3063 ns/op -BenchmarkSmall/goleveldb-0-1-1000-100-4-10/update-8 50000 37384 ns/op -BenchmarkSmall/goleveldb-0-1-1000-100-4-10/block-8 200 13892236 ns/op -Init Tree took 0.55 MB -BenchmarkSmall/memdb-100-5-1000-100-4-10/query-miss-8 500000 2794 ns/op -BenchmarkSmall/memdb-100-5-1000-100-4-10/query-hits-8 500000 3108 ns/op -BenchmarkSmall/memdb-100-5-1000-100-4-10/update-8 30000 46381 ns/op -BenchmarkSmall/memdb-100-5-1000-100-4-10/block-8 200 15793077 ns/op -Init Tree took 0.55 MB -BenchmarkSmall/goleveldb-100-5-1000-100-4-10/query-miss-8 1000000 2581 ns/op -BenchmarkSmall/goleveldb-100-5-1000-100-4-10/query-hits-8 500000 2971 ns/op -BenchmarkSmall/goleveldb-100-5-1000-100-4-10/update-8 30000 54246 ns/op -BenchmarkSmall/goleveldb-100-5-1000-100-4-10/block-8 200 17805252 ns/op -Init Tree took 0.55 MB -BenchmarkSmall/memdb-1000-10-1000-100-4-10/query-miss-8 500000 2515 ns/op -BenchmarkSmall/memdb-1000-10-1000-100-4-10/query-hits-8 500000 2713 ns/op -BenchmarkSmall/memdb-1000-10-1000-100-4-10/update-8 30000 56000 ns/op -BenchmarkSmall/memdb-1000-10-1000-100-4-10/block-8 200 18247795 ns/op -Init Tree took 0.55 MB -BenchmarkSmall/goleveldb-1000-10-1000-100-4-10/query-miss-8 500000 2406 ns/op -BenchmarkSmall/goleveldb-1000-10-1000-100-4-10/query-hits-8 500000 2716 ns/op -BenchmarkSmall/goleveldb-1000-10-1000-100-4-10/update-8 30000 54957 ns/op -BenchmarkSmall/goleveldb-1000-10-1000-100-4-10/block-8 100 10522437 ns/op -Init Tree took 0.55 MB -BenchmarkSmall/memdb-1000-1-1000-100-4-10/query-miss-8 500000 2560 ns/op -BenchmarkSmall/memdb-1000-1-1000-100-4-10/query-hits-8 500000 3053 ns/op -BenchmarkSmall/memdb-1000-1-1000-100-4-10/update-8 50000 36769 ns/op -BenchmarkSmall/memdb-1000-1-1000-100-4-10/block-8 200 14357739 ns/op -Init Tree took 0.55 MB -BenchmarkSmall/goleveldb-1000-1-1000-100-4-10/query-miss-8 500000 3290 ns/op -BenchmarkSmall/goleveldb-1000-1-1000-100-4-10/query-hits-8 500000 3437 ns/op -BenchmarkSmall/goleveldb-1000-1-1000-100-4-10/update-8 50000 56914 ns/op -BenchmarkSmall/goleveldb-1000-1-1000-100-4-10/block-8 100 15012439 ns/op -Init Tree took 0.55 MB -BenchmarkSmall/memdb-10000-100-1000-100-4-10/query-miss-8 500000 2850 ns/op -BenchmarkSmall/memdb-10000-100-1000-100-4-10/query-hits-8 500000 2730 ns/op -BenchmarkSmall/memdb-10000-100-1000-100-4-10/update-8 10000 103250 ns/op -BenchmarkSmall/memdb-10000-100-1000-100-4-10/block-8 50 39550503 ns/op -Init Tree took 0.55 MB -BenchmarkSmall/goleveldb-10000-100-1000-100-4-10/query-miss-8 500000 2684 ns/op -BenchmarkSmall/goleveldb-10000-100-1000-100-4-10/query-hits-8 500000 3280 ns/op -BenchmarkSmall/goleveldb-10000-100-1000-100-4-10/update-8 10000 107483 ns/op -BenchmarkSmall/goleveldb-10000-100-1000-100-4-10/block-8 50 40069720 ns/op -PASS -ok github.com/tendermint/iavl/benchmarks 100.688s -Init Tree took 48.86 MB -goos: darwin -goarch: amd64 -pkg: github.com/tendermint/iavl/benchmarks -BenchmarkMedium/memdb-1-0-100000-100-16-40/query-miss-8 200000 6281 ns/op -BenchmarkMedium/memdb-1-0-100000-100-16-40/query-hits-8 200000 7490 ns/op -BenchmarkMedium/memdb-1-0-100000-100-16-40/update-8 5000 589147 ns/op -BenchmarkMedium/memdb-1-0-100000-100-16-40/block-8 20 69479350 ns/op -Init Tree took 10.91 MB -BenchmarkMedium/goleveldb-1-0-100000-100-16-40/query-miss-8 50000 22021 ns/op -BenchmarkMedium/goleveldb-1-0-100000-100-16-40/query-hits-8 50000 27466 ns/op -BenchmarkMedium/goleveldb-1-0-100000-100-16-40/update-8 10000 374747 ns/op -BenchmarkMedium/goleveldb-1-0-100000-100-16-40/block-8 50 47548357 ns/op -Init Tree took 48.81 MB -BenchmarkMedium/memdb-0-1-100000-100-16-40/query-miss-8 200000 6472 ns/op -BenchmarkMedium/memdb-0-1-100000-100-16-40/query-hits-8 200000 8117 ns/op -BenchmarkMedium/memdb-0-1-100000-100-16-40/update-8 1000 1413643 ns/op -BenchmarkMedium/memdb-0-1-100000-100-16-40/block-8 20 119165460 ns/op -Init Tree took 48.87 MB -BenchmarkMedium/goleveldb-0-1-100000-100-16-40/query-miss-8 200000 6465 ns/op -BenchmarkMedium/goleveldb-0-1-100000-100-16-40/query-hits-8 200000 7681 ns/op -BenchmarkMedium/goleveldb-0-1-100000-100-16-40/update-8 2000 853301 ns/op -BenchmarkMedium/goleveldb-0-1-100000-100-16-40/block-8 20 100658873 ns/op -Init Tree took 48.85 MB -BenchmarkMedium/memdb-100-5-100000-100-16-40/query-miss-8 200000 6710 ns/op -BenchmarkMedium/memdb-100-5-100000-100-16-40/query-hits-8 200000 7424 ns/op -BenchmarkMedium/memdb-100-5-100000-100-16-40/update-8 5000 878736 ns/op -BenchmarkMedium/memdb-100-5-100000-100-16-40/block-8 20 105546937 ns/op -Init Tree took 48.84 MB -BenchmarkMedium/goleveldb-100-5-100000-100-16-40/query-miss-8 200000 6818 ns/op -BenchmarkMedium/goleveldb-100-5-100000-100-16-40/query-hits-8 200000 7820 ns/op -BenchmarkMedium/goleveldb-100-5-100000-100-16-40/update-8 5000 855615 ns/op -BenchmarkMedium/goleveldb-100-5-100000-100-16-40/block-8 20 90191776 ns/op -Init Tree took 48.86 MB -BenchmarkMedium/memdb-1000-10-100000-100-16-40/query-miss-8 200000 6628 ns/op -BenchmarkMedium/memdb-1000-10-100000-100-16-40/query-hits-8 200000 8083 ns/op -BenchmarkMedium/memdb-1000-10-100000-100-16-40/update-8 3000 900956 ns/op -BenchmarkMedium/memdb-1000-10-100000-100-16-40/block-8 20 93776159 ns/op -Init Tree took 48.87 MB -BenchmarkMedium/goleveldb-1000-10-100000-100-16-40/query-miss-8 200000 6316 ns/op -BenchmarkMedium/goleveldb-1000-10-100000-100-16-40/query-hits-8 200000 7373 ns/op -BenchmarkMedium/goleveldb-1000-10-100000-100-16-40/update-8 3000 810655 ns/op -BenchmarkMedium/goleveldb-1000-10-100000-100-16-40/block-8 20 89698234 ns/op -Init Tree took 48.83 MB -BenchmarkMedium/memdb-1000-1-100000-100-16-40/query-miss-8 200000 6414 ns/op -BenchmarkMedium/memdb-1000-1-100000-100-16-40/query-hits-8 200000 7273 ns/op -BenchmarkMedium/memdb-1000-1-100000-100-16-40/update-8 2000 793667 ns/op -BenchmarkMedium/memdb-1000-1-100000-100-16-40/block-8 20 83505306 ns/op -Init Tree took 48.88 MB -BenchmarkMedium/goleveldb-1000-1-100000-100-16-40/query-miss-8 200000 6258 ns/op -BenchmarkMedium/goleveldb-1000-1-100000-100-16-40/query-hits-8 200000 7083 ns/op -BenchmarkMedium/goleveldb-1000-1-100000-100-16-40/update-8 2000 799026 ns/op -BenchmarkMedium/goleveldb-1000-1-100000-100-16-40/block-8 20 82225915 ns/op -Init Tree took 48.88 MB -BenchmarkMedium/memdb-10000-100-100000-100-16-40/query-miss-8 200000 6420 ns/op -BenchmarkMedium/memdb-10000-100-100000-100-16-40/query-hits-8 200000 7216 ns/op -BenchmarkMedium/memdb-10000-100-100000-100-16-40/update-8 5000 595187 ns/op -BenchmarkMedium/memdb-10000-100-100000-100-16-40/block-8 20 76132336 ns/op -Init Tree took 48.86 MB -BenchmarkMedium/goleveldb-10000-100-100000-100-16-40/query-miss-8 200000 10622 ns/op -BenchmarkMedium/goleveldb-10000-100-100000-100-16-40/query-hits-8 100000 16061 ns/op -BenchmarkMedium/goleveldb-10000-100-100000-100-16-40/update-8 2000 734238 ns/op -BenchmarkMedium/goleveldb-10000-100-100000-100-16-40/block-8 20 72135954 ns/op -PASS -ok github.com/tendermint/iavl/benchmarks 124.410s -PASS -ok github.com/tendermint/iavl/benchmarks 0.015s diff --git a/benchmarks/results/158-pruning/DigitalOcean-4vcpu-8gb-160gb.txt b/benchmarks/results/158-pruning/DigitalOcean-4vcpu-8gb-160gb.txt deleted file mode 100644 index 4c56f69c6..000000000 --- a/benchmarks/results/158-pruning/DigitalOcean-4vcpu-8gb-160gb.txt +++ /dev/null @@ -1,9 +0,0 @@ -goos: linux -goarch: amd64 -pkg: github.com/tendermint/iavl/benchmarks -BenchmarkPruningStrategies/PruningStrategy{1-0}-KeyLen:16-DataLen:40-4 1 3476623971 ns/op -BenchmarkPruningStrategies/PruningStrategy{0-1}-KeyLen:16-DataLen:40-4 1 2103728119 ns/op -BenchmarkPruningStrategies/PruningStrategy{100-1}-KeyLen:16-DataLen:40-4 1 2289531671 ns/op -BenchmarkPruningStrategies/PruningStrategy{100-5}-KeyLen:16-DataLen:40-4 1 2772934060 ns/op -PASS -ok github.com/tendermint/iavl/benchmarks 21.531s diff --git a/benchmarks/results/158-pruning/sdk-pruning-1-0-results.txt b/benchmarks/results/158-pruning/sdk-pruning-1-0-results.txt deleted file mode 100644 index 99c419934..000000000 --- a/benchmarks/results/158-pruning/sdk-pruning-1-0-results.txt +++ /dev/null @@ -1,164 +0,0 @@ -Starting SimulateFromSeed with randomness created with seed 53 -Randomized simulation params: -{ - "PastEvidenceFraction": 0.5380706684387375, - "NumKeys": 130, - "EvidenceFraction": 0.38121722232625455, - "InitialLivenessWeightings": [ - 45, - 1, - 6 - ], - "LivenessTransitionMatrix": {}, - "BlockSizeTransitionMatrix": {} -} -Selected randomly generated parameters for simulated genesis: -{ - stake_per_account: "714557167989", - initially_bonded_validators: "130" -} -Selected randomly generated auth parameters: -{ - "max_memo_characters": "176", - "tx_sig_limit": "6", - "tx_size_cost_per_byte": "14", - "sig_verify_cost_ed25519": "754", - "sig_verify_cost_secp256k1": "719" -} -Selected randomly generated bank parameters: -{ - "send_enabled": true -} -Generated supply parameters: -{ - "supply": [ - { - "denom": "stake", - "amount": "185784863677140" - } - ] -} -Selected randomly generated governance parameters: -{ - "starting_proposal_id": "31", - "deposits": null, - "votes": null, - "proposals": null, - "deposit_params": { - "min_deposit": [ - { - "denom": "stake", - "amount": "496" - } - ], - "max_deposit_period": "11146000000000" - }, - "voting_params": { - "voting_period": "11146000000000" - }, - "tally_params": { - "quorum": "0.348000000000000000", - "threshold": "0.495000000000000000", - "veto": "0.288000000000000000" - } -} -Selected randomly generated minting parameters: -{ - "mint_denom": "stake", - "inflation_rate_change": "0.560000000000000000", - "inflation_max": "0.200000000000000000", - "inflation_min": "0.070000000000000000", - "goal_bonded": "0.670000000000000000", - "blocks_per_year": "6311520" -} -Selected randomly generated distribution parameters: -{ - "fee_pool": { - "community_pool": [] - }, - "community_tax": "0.090000000000000000", - "base_proposer_reward": "0.190000000000000000", - "bonus_proposer_reward": "0.100000000000000000", - "withdraw_addr_enabled": false, - "delegator_withdraw_infos": null, - "previous_proposer": "", - "outstanding_rewards": null, - "validator_accumulated_commissions": null, - "validator_historical_rewards": null, - "validator_current_rewards": null, - "delegator_starting_infos": null, - "validator_slash_events": null -} -Selected randomly generated staking parameters: -{ - "unbonding_time": "163286000000000", - "max_validators": 175, - "max_entries": 7, - "bond_denom": "stake" -} -Selected randomly generated slashing parameters: -{ - "max_evidence_age": "163286000000000", - "signed_blocks_window": "695", - "min_signed_per_window": "0.500000000000000000", - "downtime_jail_duration": "14486000000000", - "slash_fraction_double_sign": "0.024390243902439024", - "slash_fraction_downtime": "0.008130081300813008" -} -Starting the simulation from time Wed Aug 20 16:59:49 UTC 2566 (unixtime 18828003589) - -Simulating... block 8063/10000, operation 0/349. - -Simulation stopped early as all validators have been unbonded; nobody left to propose a block! -Event statistics: - /no-operation/failure => 39403 - auth/deduct_fee/failure => 232 - auth/deduct_fee/ok => 3501 - bank/multisend/failure => 388 - bank/multisend/ok => 5802 - bank/send/failure => 3773 - bank/send/ok => 59815 - beginblock/evidence => 4927 - beginblock/signing/missed => 2741 - beginblock/signing/signed => 19978 - distribution/set_withdraw_address/failure => 31822 - distribution/withdraw_delegator_reward/failure => 8225 - distribution/withdraw_delegator_reward/ok => 23820 - distribution/withdraw_validator_commission/failure => 30578 - distribution/withdraw_validator_commission/ok => 1213 - endblock/validatorupdates/added => 35 - endblock/validatorupdates/kicked => 165 - endblock/validatorupdates/updated => 3909 - gov/deposit/failure => 63464 - gov/deposit/ok => 341 - gov/submit_proposal/failure => 2002 - gov/submit_proposal/ok => 7442 - slashing/unjail/failure => 63077 - slashing/unjail/ok => 29 - staking/begin_redelegate/failure => 32582 - staking/begin_redelegate/ok => 21364 - staking/begin_unbonding/failure => 5 - staking/begin_unbonding/ok => 53397 - staking/create_validator/failure => 53883 - staking/create_validator/ok => 6 - staking/delegate/ok => 53694 - staking/edit_validator/failure => 380 - staking/edit_validator/ok => 2882 -GoLevelDB Stats -Compactions - Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB) --------+------------+---------------+---------------+---------------+--------------- - 0 | 0 | 0.00000 | 19.52257 | 0.00000 | 1409.10107 - 1 | 78 | 100.56797 | 956.69678 | 49073.19592 | 49085.70664 - 2 | 542 | 1000.69145 | 1425.27505 | 112484.45822 | 112493.21130 - 3 | 166 | 329.11150 | 15.28762 | 1024.58862 | 1024.59469 --------+------------+---------------+---------------+---------------+--------------- - Total | 786 | 1430.37092 | 2416.78201 | 162582.24277 | 164012.61369 - -GoLevelDB cached block size 8373420 -goos: linux -goarch: amd64 -pkg: github.com/cosmos/cosmos-sdk/simapp -BenchmarkFullAppSimulation-4 1 2925903867806 ns/op 359678535952 B/op 6131788601 allocs/op -PASS -ok github.com/cosmos/cosmos-sdk/simapp 2926.999s diff --git a/benchmarks/results/158-pruning/sdk-pruning-10-2-results.txt b/benchmarks/results/158-pruning/sdk-pruning-10-2-results.txt deleted file mode 100644 index 781197b93..000000000 --- a/benchmarks/results/158-pruning/sdk-pruning-10-2-results.txt +++ /dev/null @@ -1,163 +0,0 @@ -Starting SimulateFromSeed with randomness created with seed 53 -Randomized simulation params: -{ - "PastEvidenceFraction": 0.5380706684387375, - "NumKeys": 130, - "EvidenceFraction": 0.38121722232625455, - "InitialLivenessWeightings": [ - 45, - 1, - 6 - ], - "LivenessTransitionMatrix": {}, - "BlockSizeTransitionMatrix": {} -} -Selected randomly generated parameters for simulated genesis: -{ - stake_per_account: "714557167989", - initially_bonded_validators: "130" -} -Selected randomly generated auth parameters: -{ - "max_memo_characters": "176", - "tx_sig_limit": "6", - "tx_size_cost_per_byte": "14", - "sig_verify_cost_ed25519": "754", - "sig_verify_cost_secp256k1": "719" -} -Selected randomly generated bank parameters: -{ - "send_enabled": true -} -Generated supply parameters: -{ - "supply": [ - { - "denom": "stake", - "amount": "185784863677140" - } - ] -} -Selected randomly generated governance parameters: -{ - "starting_proposal_id": "31", - "deposits": null, - "votes": null, - "proposals": null, - "deposit_params": { - "min_deposit": [ - { - "denom": "stake", - "amount": "496" - } - ], - "max_deposit_period": "11146000000000" - }, - "voting_params": { - "voting_period": "11146000000000" - }, - "tally_params": { - "quorum": "0.348000000000000000", - "threshold": "0.495000000000000000", - "veto": "0.288000000000000000" - } -} -Selected randomly generated minting parameters: -{ - "mint_denom": "stake", - "inflation_rate_change": "0.560000000000000000", - "inflation_max": "0.200000000000000000", - "inflation_min": "0.070000000000000000", - "goal_bonded": "0.670000000000000000", - "blocks_per_year": "6311520" -} -Selected randomly generated distribution parameters: -{ - "fee_pool": { - "community_pool": [] - }, - "community_tax": "0.090000000000000000", - "base_proposer_reward": "0.190000000000000000", - "bonus_proposer_reward": "0.100000000000000000", - "withdraw_addr_enabled": false, - "delegator_withdraw_infos": null, - "previous_proposer": "", - "outstanding_rewards": null, - "validator_accumulated_commissions": null, - "validator_historical_rewards": null, - "validator_current_rewards": null, - "delegator_starting_infos": null, - "validator_slash_events": null -} -Selected randomly generated staking parameters: -{ - "unbonding_time": "163286000000000", - "max_validators": 175, - "max_entries": 7, - "bond_denom": "stake" -} -Selected randomly generated slashing parameters: -{ - "max_evidence_age": "163286000000000", - "signed_blocks_window": "695", - "min_signed_per_window": "0.500000000000000000", - "downtime_jail_duration": "14486000000000", - "slash_fraction_double_sign": "0.024390243902439024", - "slash_fraction_downtime": "0.008130081300813008" -} -Starting the simulation from time Wed Aug 20 16:59:49 UTC 2566 (unixtime 18828003589) - -Simulating... block 8063/10000, operation 0/349. - -Simulation stopped early as all validators have been unbonded; nobody left to propose a block! -Event statistics: - /no-operation/failure => 39403 - auth/deduct_fee/failure => 232 - auth/deduct_fee/ok => 3501 - bank/multisend/failure => 388 - bank/multisend/ok => 5802 - bank/send/failure => 3773 - bank/send/ok => 59815 - beginblock/evidence => 4927 - beginblock/signing/missed => 2741 - beginblock/signing/signed => 19978 - distribution/set_withdraw_address/failure => 31822 - distribution/withdraw_delegator_reward/failure => 8225 - distribution/withdraw_delegator_reward/ok => 23820 - distribution/withdraw_validator_commission/failure => 30578 - distribution/withdraw_validator_commission/ok => 1213 - endblock/validatorupdates/added => 35 - endblock/validatorupdates/kicked => 165 - endblock/validatorupdates/updated => 3909 - gov/deposit/failure => 63464 - gov/deposit/ok => 341 - gov/submit_proposal/failure => 2002 - gov/submit_proposal/ok => 7442 - slashing/unjail/failure => 63077 - slashing/unjail/ok => 29 - staking/begin_redelegate/failure => 32582 - staking/begin_redelegate/ok => 21364 - staking/begin_unbonding/failure => 5 - staking/begin_unbonding/ok => 53397 - staking/create_validator/failure => 53883 - staking/create_validator/ok => 6 - staking/delegate/ok => 53694 - staking/edit_validator/failure => 380 - staking/edit_validator/ok => 2882 -GoLevelDB Stats -Compactions - Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB) --------+------------+---------------+---------------+---------------+--------------- - 0 | 3 | 8.20059 | 76.03062 | 0.00000 | 6397.45456 - 1 | 60 | 99.32366 | 969.71138 | 65637.01816 | 59966.39793 - 2 | 273 | 541.33349 | 34.51034 | 2668.15302 | 2590.17643 --------+------------+---------------+---------------+---------------+--------------- - Total | 336 | 648.85774 | 1080.25234 | 68305.17119 | 68954.02892 - -GoLevelDB cached block size 0 -goos: linux -goarch: amd64 -pkg: github.com/cosmos/cosmos-sdk/simapp -BenchmarkFullAppSimulation-4 1 2880812940613 ns/op 458329227160 B/op 7859938088 allocs/op -PASS -ok github.com/cosmos/cosmos-sdk/simapp 2881.646s diff --git a/benchmarks/results/158-pruning/sdk-pruning-100-2-results.txt b/benchmarks/results/158-pruning/sdk-pruning-100-2-results.txt deleted file mode 100644 index a0c05690e..000000000 --- a/benchmarks/results/158-pruning/sdk-pruning-100-2-results.txt +++ /dev/null @@ -1,163 +0,0 @@ -Starting SimulateFromSeed with randomness created with seed 53 -Randomized simulation params: -{ - "PastEvidenceFraction": 0.5380706684387375, - "NumKeys": 130, - "EvidenceFraction": 0.38121722232625455, - "InitialLivenessWeightings": [ - 45, - 1, - 6 - ], - "LivenessTransitionMatrix": {}, - "BlockSizeTransitionMatrix": {} -} -Selected randomly generated parameters for simulated genesis: -{ - stake_per_account: "714557167989", - initially_bonded_validators: "130" -} -Selected randomly generated auth parameters: -{ - "max_memo_characters": "176", - "tx_sig_limit": "6", - "tx_size_cost_per_byte": "14", - "sig_verify_cost_ed25519": "754", - "sig_verify_cost_secp256k1": "719" -} -Selected randomly generated bank parameters: -{ - "send_enabled": true -} -Generated supply parameters: -{ - "supply": [ - { - "denom": "stake", - "amount": "185784863677140" - } - ] -} -Selected randomly generated governance parameters: -{ - "starting_proposal_id": "31", - "deposits": null, - "votes": null, - "proposals": null, - "deposit_params": { - "min_deposit": [ - { - "denom": "stake", - "amount": "496" - } - ], - "max_deposit_period": "11146000000000" - }, - "voting_params": { - "voting_period": "11146000000000" - }, - "tally_params": { - "quorum": "0.348000000000000000", - "threshold": "0.495000000000000000", - "veto": "0.288000000000000000" - } -} -Selected randomly generated minting parameters: -{ - "mint_denom": "stake", - "inflation_rate_change": "0.560000000000000000", - "inflation_max": "0.200000000000000000", - "inflation_min": "0.070000000000000000", - "goal_bonded": "0.670000000000000000", - "blocks_per_year": "6311520" -} -Selected randomly generated distribution parameters: -{ - "fee_pool": { - "community_pool": [] - }, - "community_tax": "0.090000000000000000", - "base_proposer_reward": "0.190000000000000000", - "bonus_proposer_reward": "0.100000000000000000", - "withdraw_addr_enabled": false, - "delegator_withdraw_infos": null, - "previous_proposer": "", - "outstanding_rewards": null, - "validator_accumulated_commissions": null, - "validator_historical_rewards": null, - "validator_current_rewards": null, - "delegator_starting_infos": null, - "validator_slash_events": null -} -Selected randomly generated staking parameters: -{ - "unbonding_time": "163286000000000", - "max_validators": 175, - "max_entries": 7, - "bond_denom": "stake" -} -Selected randomly generated slashing parameters: -{ - "max_evidence_age": "163286000000000", - "signed_blocks_window": "695", - "min_signed_per_window": "0.500000000000000000", - "downtime_jail_duration": "14486000000000", - "slash_fraction_double_sign": "0.024390243902439024", - "slash_fraction_downtime": "0.008130081300813008" -} -Starting the simulation from time Wed Aug 20 16:59:49 UTC 2566 (unixtime 18828003589) - -Simulating... block 8063/10000, operation 0/349. - -Simulation stopped early as all validators have been unbonded; nobody left to propose a block! -Event statistics: - /no-operation/failure => 39403 - auth/deduct_fee/failure => 232 - auth/deduct_fee/ok => 3501 - bank/multisend/failure => 388 - bank/multisend/ok => 5802 - bank/send/failure => 3773 - bank/send/ok => 59815 - beginblock/evidence => 4927 - beginblock/signing/missed => 2741 - beginblock/signing/signed => 19978 - distribution/set_withdraw_address/failure => 31822 - distribution/withdraw_delegator_reward/failure => 8225 - distribution/withdraw_delegator_reward/ok => 23820 - distribution/withdraw_validator_commission/failure => 30578 - distribution/withdraw_validator_commission/ok => 1213 - endblock/validatorupdates/added => 35 - endblock/validatorupdates/kicked => 165 - endblock/validatorupdates/updated => 3909 - gov/deposit/failure => 63464 - gov/deposit/ok => 341 - gov/submit_proposal/failure => 2002 - gov/submit_proposal/ok => 7442 - slashing/unjail/failure => 63077 - slashing/unjail/ok => 29 - staking/begin_redelegate/failure => 32582 - staking/begin_redelegate/ok => 21364 - staking/begin_unbonding/failure => 5 - staking/begin_unbonding/ok => 53397 - staking/create_validator/failure => 53883 - staking/create_validator/ok => 6 - staking/delegate/ok => 53694 - staking/edit_validator/failure => 380 - staking/edit_validator/ok => 2882 -GoLevelDB Stats -Compactions - Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB) --------+------------+---------------+---------------+---------------+--------------- - 0 | 0 | 0.00000 | 10.04960 | 0.00000 | 766.38953 - 1 | 54 | 98.12634 | 136.22127 | 8386.60535 | 7919.98135 - 2 | 97 | 186.67873 | 4.74288 | 333.74171 | 318.78126 --------+------------+---------------+---------------+---------------+--------------- - Total | 151 | 284.80508 | 151.01376 | 8720.34706 | 9005.15214 - -GoLevelDB cached block size 0 -goos: linux -goarch: amd64 -pkg: github.com/cosmos/cosmos-sdk/simapp -BenchmarkFullAppSimulation-4 1 2705549310442 ns/op 325911847648 B/op 6971455548 allocs/op -PASS -ok github.com/cosmos/cosmos-sdk/simapp 2706.410s diff --git a/benchmarks/results/158-pruning/sdk-pruning-1000-5-results.txt b/benchmarks/results/158-pruning/sdk-pruning-1000-5-results.txt deleted file mode 100644 index 2d069d16f..000000000 --- a/benchmarks/results/158-pruning/sdk-pruning-1000-5-results.txt +++ /dev/null @@ -1,162 +0,0 @@ -Starting SimulateFromSeed with randomness created with seed 53 -Randomized simulation params: -{ - "PastEvidenceFraction": 0.5380706684387375, - "NumKeys": 130, - "EvidenceFraction": 0.38121722232625455, - "InitialLivenessWeightings": [ - 45, - 1, - 6 - ], - "LivenessTransitionMatrix": {}, - "BlockSizeTransitionMatrix": {} -} -Selected randomly generated parameters for simulated genesis: -{ - stake_per_account: "714557167989", - initially_bonded_validators: "130" -} -Selected randomly generated auth parameters: -{ - "max_memo_characters": "176", - "tx_sig_limit": "6", - "tx_size_cost_per_byte": "14", - "sig_verify_cost_ed25519": "754", - "sig_verify_cost_secp256k1": "719" -} -Selected randomly generated bank parameters: -{ - "send_enabled": true -} -Generated supply parameters: -{ - "supply": [ - { - "denom": "stake", - "amount": "185784863677140" - } - ] -} -Selected randomly generated governance parameters: -{ - "starting_proposal_id": "31", - "deposits": null, - "votes": null, - "proposals": null, - "deposit_params": { - "min_deposit": [ - { - "denom": "stake", - "amount": "496" - } - ], - "max_deposit_period": "11146000000000" - }, - "voting_params": { - "voting_period": "11146000000000" - }, - "tally_params": { - "quorum": "0.348000000000000000", - "threshold": "0.495000000000000000", - "veto": "0.288000000000000000" - } -} -Selected randomly generated minting parameters: -{ - "mint_denom": "stake", - "inflation_rate_change": "0.560000000000000000", - "inflation_max": "0.200000000000000000", - "inflation_min": "0.070000000000000000", - "goal_bonded": "0.670000000000000000", - "blocks_per_year": "6311520" -} -Selected randomly generated distribution parameters: -{ - "fee_pool": { - "community_pool": [] - }, - "community_tax": "0.090000000000000000", - "base_proposer_reward": "0.190000000000000000", - "bonus_proposer_reward": "0.100000000000000000", - "withdraw_addr_enabled": false, - "delegator_withdraw_infos": null, - "previous_proposer": "", - "outstanding_rewards": null, - "validator_accumulated_commissions": null, - "validator_historical_rewards": null, - "validator_current_rewards": null, - "delegator_starting_infos": null, - "validator_slash_events": null -} -Selected randomly generated staking parameters: -{ - "unbonding_time": "163286000000000", - "max_validators": 175, - "max_entries": 7, - "bond_denom": "stake" -} -Selected randomly generated slashing parameters: -{ - "max_evidence_age": "163286000000000", - "signed_blocks_window": "695", - "min_signed_per_window": "0.500000000000000000", - "downtime_jail_duration": "14486000000000", - "slash_fraction_double_sign": "0.024390243902439024", - "slash_fraction_downtime": "0.008130081300813008" -} -Starting the simulation from time Wed Aug 20 16:59:49 UTC 2566 (unixtime 18828003589) - -Simulating... block 8063/10000, operation 0/349. - -Simulation stopped early as all validators have been unbonded; nobody left to propose a block! -Event statistics: - /no-operation/failure => 39403 - auth/deduct_fee/failure => 232 - auth/deduct_fee/ok => 3501 - bank/multisend/failure => 388 - bank/multisend/ok => 5802 - bank/send/failure => 3773 - bank/send/ok => 59815 - beginblock/evidence => 4927 - beginblock/signing/missed => 2741 - beginblock/signing/signed => 19978 - distribution/set_withdraw_address/failure => 31822 - distribution/withdraw_delegator_reward/failure => 8225 - distribution/withdraw_delegator_reward/ok => 23820 - distribution/withdraw_validator_commission/failure => 30578 - distribution/withdraw_validator_commission/ok => 1213 - endblock/validatorupdates/added => 35 - endblock/validatorupdates/kicked => 165 - endblock/validatorupdates/updated => 3909 - gov/deposit/failure => 63464 - gov/deposit/ok => 341 - gov/submit_proposal/failure => 2002 - gov/submit_proposal/ok => 7442 - slashing/unjail/failure => 63077 - slashing/unjail/ok => 29 - staking/begin_redelegate/failure => 32582 - staking/begin_redelegate/ok => 21364 - staking/begin_unbonding/failure => 5 - staking/begin_unbonding/ok => 53397 - staking/create_validator/failure => 53883 - staking/create_validator/ok => 6 - staking/delegate/ok => 53694 - staking/edit_validator/failure => 380 - staking/edit_validator/ok => 2882 -GoLevelDB Stats -Compactions - Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB) --------+------------+---------------+---------------+---------------+--------------- - 0 | 1 | 1.77039 | 1.58206 | 0.00000 | 108.22015 - 1 | 47 | 93.77884 | 12.13807 | 666.67667 | 654.00576 --------+------------+---------------+---------------+---------------+--------------- - Total | 48 | 95.54923 | 13.72012 | 666.67667 | 762.22590 - -GoLevelDB cached block size 0 -goos: linux -goarch: amd64 -pkg: github.com/cosmos/cosmos-sdk/simapp -BenchmarkFullAppSimulation-4 1 2657169989384 ns/op 315683118736 B/op 6960674109 allocs/op -PASS -ok github.com/cosmos/cosmos-sdk/simapp 2658.013s diff --git a/benchmarks/results/158-pruning/sdk-pruning-5-2-results.txt b/benchmarks/results/158-pruning/sdk-pruning-5-2-results.txt deleted file mode 100644 index de8572fdf..000000000 --- a/benchmarks/results/158-pruning/sdk-pruning-5-2-results.txt +++ /dev/null @@ -1,163 +0,0 @@ -Starting SimulateFromSeed with randomness created with seed 53 -Randomized simulation params: -{ - "PastEvidenceFraction": 0.5380706684387375, - "NumKeys": 130, - "EvidenceFraction": 0.38121722232625455, - "InitialLivenessWeightings": [ - 45, - 1, - 6 - ], - "LivenessTransitionMatrix": {}, - "BlockSizeTransitionMatrix": {} -} -Selected randomly generated parameters for simulated genesis: -{ - stake_per_account: "714557167989", - initially_bonded_validators: "130" -} -Selected randomly generated auth parameters: -{ - "max_memo_characters": "176", - "tx_sig_limit": "6", - "tx_size_cost_per_byte": "14", - "sig_verify_cost_ed25519": "754", - "sig_verify_cost_secp256k1": "719" -} -Selected randomly generated bank parameters: -{ - "send_enabled": true -} -Generated supply parameters: -{ - "supply": [ - { - "denom": "stake", - "amount": "185784863677140" - } - ] -} -Selected randomly generated governance parameters: -{ - "starting_proposal_id": "31", - "deposits": null, - "votes": null, - "proposals": null, - "deposit_params": { - "min_deposit": [ - { - "denom": "stake", - "amount": "496" - } - ], - "max_deposit_period": "11146000000000" - }, - "voting_params": { - "voting_period": "11146000000000" - }, - "tally_params": { - "quorum": "0.348000000000000000", - "threshold": "0.495000000000000000", - "veto": "0.288000000000000000" - } -} -Selected randomly generated minting parameters: -{ - "mint_denom": "stake", - "inflation_rate_change": "0.560000000000000000", - "inflation_max": "0.200000000000000000", - "inflation_min": "0.070000000000000000", - "goal_bonded": "0.670000000000000000", - "blocks_per_year": "6311520" -} -Selected randomly generated distribution parameters: -{ - "fee_pool": { - "community_pool": [] - }, - "community_tax": "0.090000000000000000", - "base_proposer_reward": "0.190000000000000000", - "bonus_proposer_reward": "0.100000000000000000", - "withdraw_addr_enabled": false, - "delegator_withdraw_infos": null, - "previous_proposer": "", - "outstanding_rewards": null, - "validator_accumulated_commissions": null, - "validator_historical_rewards": null, - "validator_current_rewards": null, - "delegator_starting_infos": null, - "validator_slash_events": null -} -Selected randomly generated staking parameters: -{ - "unbonding_time": "163286000000000", - "max_validators": 175, - "max_entries": 7, - "bond_denom": "stake" -} -Selected randomly generated slashing parameters: -{ - "max_evidence_age": "163286000000000", - "signed_blocks_window": "695", - "min_signed_per_window": "0.500000000000000000", - "downtime_jail_duration": "14486000000000", - "slash_fraction_double_sign": "0.024390243902439024", - "slash_fraction_downtime": "0.008130081300813008" -} -Starting the simulation from time Wed Aug 20 16:59:49 UTC 2566 (unixtime 18828003589) - -Simulating... block 8063/10000, operation 0/349. - -Simulation stopped early as all validators have been unbonded; nobody left to propose a block! -Event statistics: - /no-operation/failure => 39403 - auth/deduct_fee/failure => 232 - auth/deduct_fee/ok => 3501 - bank/multisend/failure => 388 - bank/multisend/ok => 5802 - bank/send/failure => 3773 - bank/send/ok => 59815 - beginblock/evidence => 4927 - beginblock/signing/missed => 2741 - beginblock/signing/signed => 19978 - distribution/set_withdraw_address/failure => 31822 - distribution/withdraw_delegator_reward/failure => 8225 - distribution/withdraw_delegator_reward/ok => 23820 - distribution/withdraw_validator_commission/failure => 30578 - distribution/withdraw_validator_commission/ok => 1213 - endblock/validatorupdates/added => 35 - endblock/validatorupdates/kicked => 165 - endblock/validatorupdates/updated => 3909 - gov/deposit/failure => 63464 - gov/deposit/ok => 341 - gov/submit_proposal/failure => 2002 - gov/submit_proposal/ok => 7442 - slashing/unjail/failure => 63077 - slashing/unjail/ok => 29 - staking/begin_redelegate/failure => 32582 - staking/begin_redelegate/ok => 21364 - staking/begin_unbonding/failure => 5 - staking/begin_unbonding/ok => 53397 - staking/create_validator/failure => 53883 - staking/create_validator/ok => 6 - staking/delegate/ok => 53694 - staking/edit_validator/failure => 380 - staking/edit_validator/ok => 2882 -GoLevelDB Stats -Compactions - Level | Tables | Size(MB) | Time(sec) | Read(MB) | Write(MB) --------+------------+---------------+---------------+---------------+--------------- - 0 | 3 | 8.20775 | 157.18686 | 0.00000 | 12515.15473 - 1 | 65 | 99.16383 | 1962.98229 | 129484.30484 | 117927.78594 - 2 | 368 | 733.14210 | 58.03251 | 4574.45495 | 4456.33280 --------+------------+---------------+---------------+---------------+--------------- - Total | 436 | 840.51368 | 2178.20165 | 134058.75979 | 134899.27347 - -GoLevelDB cached block size 0 -goos: linux -goarch: amd64 -pkg: github.com/cosmos/cosmos-sdk/simapp -BenchmarkFullAppSimulation-4 1 3603346114000 ns/op 606564761648 B/op 8823765100 allocs/op -PASS -ok github.com/cosmos/cosmos-sdk/simapp 3604.335s diff --git a/doc.go b/doc.go index 496faca81..7786a3d32 100644 --- a/doc.go +++ b/doc.go @@ -4,7 +4,7 @@ // The tree is not safe for concurrent use, and must be guarded by a Mutex // or RWLock as appropriate - the exception is immutable trees returned by // MutableTree.GetImmutable() which are safe for concurrent use as long as -// the version is not deleted via DeleteVersion() or pruning settings. +// the version is not deleted via DeleteVersion(). // // // Basic usage of MutableTree: diff --git a/docs/architecture/adr-001-flush-version.md b/docs/architecture/adr-001-flush-version.md deleted file mode 100644 index 675dd04db..000000000 --- a/docs/architecture/adr-001-flush-version.md +++ /dev/null @@ -1,140 +0,0 @@ -# ADR 001: Flush Version - -## Changelog - -- May 22, 2020: Initial Draft - -## Status - -Proposed - -## Context - -The IAVL library recently underwent changes to the pruning and version commitment model. Specifically, -the invariant that a version is flushed to disk when it is committed via `SaveVersion` no longer holds -true. Instead, versions are kept in memory and periodically flushed to disk and then pruned based on -the client supplied pruning strategy parameters. For more detailed information, see the -[PRUNING](../tree/PRUNING.md) document. - -These changes, while drastically improving performance under certain circumstances, introduces certain -tradeoffs. Specifically, an application with a deterministic state-machine that commits and merkle-izes -state via IAVL tree(s), can no longer guarantee that a state is written to disk when it is committed -and must rely on some sort of state replay mechanism (e.g. Tendermint peer-to-peer gossip & state machine execution). -While this inherently is not necessarily a problem, it becomes a problem under certain contexts that -depend on a specific version existing on disk. - -One such example is live upgrades. Specifically, when a live upgrade occurs in some application, typically -a new binary will be started in place of the old binary and some set of business logic will need to -be executed in order to migrate and handle old and/or invalid state to make it compatible with the -new version. Being that these operations must occur on the latest canonical state, we must ensure the -latest version is committed and flushed to disk so all operators and clients see the same view when -doing this upgrade. Hence, we need a means to manually signal to the IAVL tree that a specific -version should be flushed to disk. In addition, we also need to ensure this version is not pruned at -a later point in time. - -## Decision - -We propose to introduce a new API, `FlushVersion(version int64)`. This new API will attempt to manually -flush a previously committed version to disk. It will return an error if the version does not exist -in memory or if the underlying flush fails. If the version is already flushed, the method performs a -no-op and no error is returned. After the version is flushed to disk and the version is not the latest -version, we will also remove the version from the recent (in-memory) database via `DeleteVersionFromRecent`. - -In order to not have a manually flushed version pruned at a later point in time -(e.g. after an application restarts), we also introduce a new type, `VersionMetadata`, that will be -flushed to disk for every version saved, regardless if the version is flushed to disk. The `VersionMetadata` -type will also provide us with added benefits around pruning model and how clients treat snapshots. - -```go -type VersionMetadata struct { - Version int64 // tree version for the corresponding metadata - Committed int64 // the UNIX timestamp of when the metadata was committed to disk - Updated int64 // the UNIX timestamp of when the metadata was updated - RootHash []byte // the root hash of the tree for the corresponding metadata - Snapshot bool // if this version corresponds to a version that is flushed to disk -} - -func VersionMetadataKey(version int64) []byte { - return []byte(fmt.Sprintf("metadata/%d", version)) -} -``` - -Currently, during `MutableTree#SaveVersion` we write to both a recentDB and a snapshotDB if the -version in question is meant to be a snapshot version (i.e. by calling `isSnapshotVersion`). Afterwards, -we check if any recent version needs to be pruned from memory by calling `PruneRecentVersions`. -In addition, `isSnapshotVersion` calls are found throughout the internal API and are posed to be -problematic when the underlying pruning strategy changes -(i.e. a previous call that returned `true` for a version may now return `false`). As a result, the -internal IAVL pruning model can be hard to follow and may break invariants under certain circumstances. - -The `VersionMetadata` can drastically simplify a lot of this logic. Namely, we propose to remove -`isSnapshotVersion` and instead set `VersionMetadata.Snapshot` when creating the initial `VersionMetadata` -during `MutableTree#SaveVersion`. - -Now everywhere `isSnapshotVersion` is called, we can now simply refer to `VersionMetadata.Snapshot` -instead. `VersionMetadata` will be managed by the `MutableTree` and will be saved to disk via -`nodeDB.SnapshotDB` during all of the following phases: - -- At the end of `MutableTree#SaveVersion` - - The fields `Version`, `Snapshot`, `Committed`, `Updated`, and `RootHash` will be set here. -- During `MutableTree#PruneRecentVersion` - - The field `Updated` will be set here. -- During `MutableTree#DeleteVersion` - - The fields `Updated` and `Snapshot` will be set here. -- During `MutableTree#FlushVersion` - - The fields `Updated` and `Snapshot` will be set here. - -Note, at no point do we delete `VersionMetadata` for any given version. We will expose an API through -`MutableTree` that allows clients to fetch `VersionMetadata` for a given version so that any upstream -business logic may rely on this new type (e.g. deleting snapshots). Anytime existing `VersionMetadata` -is updated (e.g. when pruned or deleted), the `VersionMetadata.Updated` will reflect the timestamp of -this event. - -Finally, we will serialize the `VersionMetadata` type using Protocol Buffers and we will also utilize -a write-through LRU cache within `nodeDB` for getting and setting `VersionMetadata` objects. - -## Future Improvements - -Upstream clients and applications may further expand on IAVL pruning and related functionality. In -addition, these clients may wish to query `VersionMetadata` to know more information about any given -committed version. In the future, we may consider introducing a `Status` into the `VersionMetadata`: - -```go -type VersionStatus string - -const ( - Saving VersionStatus = "saving" // set when a version is about to be saved - Buffered VersionStatus = "buffered" // set when a version has been saved to volatile storage - Saved VersionStatus = "saved" // set when a version has been saved to disk - Pruned VersionStatus = "pruned" // set when a version has been pruned - Deleted VersionStatus = "deleted" // set when a version has been explicitly deleted -) - -type VersionMetadata struct { - // ... - Status VersionStatus // the status of the version -} -``` - -Now, whenever a version is pruned or explicitly deleted, the `VersionMetadata.Status` will set to -`Pruned` or `Deleted` respectively. - -## Consequences - -### Positive - -- Allows use to flush manually committed versions to disk at will -- Control over pruning of manually flushed committed versions -- Removal of error-prone `isSnapshotVersion` -- Allowing clients to utilize `VersionMetadata` to make decisions on committed state - -### Negative - -- Additional storage per committed version -- One additional DB lookup during `PruneRecentVersions` - - We can use a fixed-size LRU cache for `CommitMetadata` to nullify most of the - IO impact here. - -### Neutral - -## References diff --git a/docs/node/node.md b/docs/node/node.md index f75f42442..64d19614c 100644 --- a/docs/node/node.md +++ b/docs/node/node.md @@ -17,7 +17,6 @@ type Node struct { leftNode *Node // pointer to left child rightHash []byte // hash of right child rightNode *Node // pointer to right child - saved bool // saved to memory or disk persisted bool // persisted to disk } ``` @@ -132,9 +131,3 @@ func (node *Node) writeHashBytes(w io.Writer) error { return nil } ``` - -### Saving vs Persisting - -Nodes may be saved in memory or in persistent storage. If the node has been written to memory *or* storage, `node.saved = true`. - -`node.persisted = true` only if the node gets written to persistent storage. diff --git a/docs/node/nodedb.md b/docs/node/nodedb.md index 30ffaf802..2224d35cb 100644 --- a/docs/node/nodedb.md +++ b/docs/node/nodedb.md @@ -2,16 +2,10 @@ ### Structure -The nodeDB is responsible for persisting nodes, orphans, and roots correctly in temporary and persistent storage. It also handles all pruning logic given a passed in pruning strategy. The default behavior is to prune nothing and persist every version to disk. - -The nodeDB maintains two seperate databases and batches. The `recentDB` is for temporary storage (typically `memDB`) and the `snapshotDB` is for persistent storage (typically `levelDB`). - -The nodeDB takes in a PruningStrategy to determine how many recent versions to save and at what frequency to persist to disk. Recent versions are saved in `memDB`, while snapshot versions are persisted to disk. +The nodeDB is responsible for persisting nodes, orphans, and roots correctly in persistent storage. ### Saving Versions -When an IAVL tree is saved, the nodeDB first checks the version against the PruningStrategy. If the version is a snapshot version, then the IAVL tree gets saved both to the `recentDB` and `snapshotDB`. If not, the tree is saved only to the `recentDB`. - The nodeDB saves the roothash of the IAVL tree under the key: `r|`. It marshals and saves any new node that has been created under: `n|`. For more details on how the node gets marshaled, see [node documentation](./node.md). Any old node that is still part of the latest IAVL tree will not get rewritten. Instead its parent will simply have a hash pointer with which the nodeDB can retrieve the old node if necessary. @@ -20,37 +14,10 @@ Any old nodes that were part of the previous version IAVL but are no longer part (For more details on key formats see the [keyformat docs](./key_format.md)) -##### Persisting Orphans to Disk - -The SaveOrphans algorithm works slightly differently when a custom PruningStrategy is used such that `strategy.KeepEvery > 1`. - -If a snapshot version exists between an orphans `fromVersion` and `toVersion` inclusive, then that orphan needs to be persisted to disk since a snapshot version refers to it. - -However, it will get persisted to disk with a toVersion equal to the highest snapshot version that contains it rather than the generally highest predecessor. - -```golang -// Logic for determining if orphan should be persisted to disk -flushToDisk := false -if ndb.opts.KeepEvery != 0 { - // if snapshot version in between fromVersion and toVersion INCLUSIVE, then flush to disk. - flushToDisk = fromVersion/ndb.opts.KeepEvery != toVersion/ndb.opts.KeepEvery || ndb.isSnapshotVersion(fromVersion) -} -``` - -```golang -// Logic for saving to disk with toVersion that is highest snapshotVersion <= original toVersion -// save to disk with toVersion equal to snapshotVersion closest to original toVersion -snapVersion := toVersion - (toVersion % ndb.opts.KeepEvery) -key := ndb.orphanKey(fromVersion, snapVersion, hash) -ndb.snapshotBatch.Set(key, hash) -``` - ### Deleting Versions When a version `v` is deleted, the roothash corresponding to version `v` is deleted from nodeDB. All orphans whose `toVersion = v`, will get the `toVersion` pushed back to the highest predecessor of `v` that still exists in nodeDB. If the `toVersion <= fromVersion` then this implies that there does not exist a version of the IAVL tree in the nodeDB that still contains this node. Thus, it can be safely deleted and uncached. -When deleting a version, the caller can specify if the deletion is supposed to be from `memoryOnly` or completely deleted. If `memOnly` argument is true, then deletion only happens on `recentDB`. Else, IAVL version gets deleted both from `recentDB` and `snapshotDB`. - ##### Deleting Orphans The deleteOrphans algorithm is shown below: @@ -87,12 +54,3 @@ func (ndb *nodeDB) deleteOrphans(version int64) { }) } ``` - -### Pruning Versions - -If the nodeDB is passed in a PruningStrategy with `strategy.keepRecent != 0`, it will maintain the specified number of recent versions in the memDB. -When `PruneRecentVersions`, the IAVL version `v - strategy.keepRecent` will be deleted from `recentDB` (Calls `DeleteVersion(v - strategy.KeepRecent, memOnly=true)`). This ensures that at any given point, there are only `strategy.keepRecent` versions in `recentDB`. - -Note this is not called immediately in `nodeDB`, the caller (in most cases `MutableTree`) is responsible for calling `PruneRecentVersions` after each save to ensure that `recentDB` is always holding at most `keepRecent` versions. - -`PruneRecentVersions` will return the version numbers that no longer exist in the nodeDB. If the version that got pruned is a snapshot version, `PruneRecentVersions` returns `nil` since the version will still exist in the `snapshotDB`. Else, `PruneRecentVersions` will return a list containing the pruned version number. diff --git a/docs/overview.md b/docs/overview.md index 9ae6991ce..8d3fb1428 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -24,7 +24,6 @@ In Ethereum, the analog is [Patricia tries](http://en.wikipedia.org/wiki/Radix_t 3. [NodeDB docs](./node/nodedb.md): - Explains how nodes, orphans, roots get saved in database - Explains saving and deleting tree logic. - - Explains how pruning IAVL tree works 4. [ImmutableTree docs](./tree/immutable_tree.md) - Explains ImmutableTree structure - Explains ImmutableTree Iteration functions diff --git a/docs/tree/PRUNING.md b/docs/tree/PRUNING.md deleted file mode 100644 index 5fc620262..000000000 --- a/docs/tree/PRUNING.md +++ /dev/null @@ -1,41 +0,0 @@ -# Pruning - -Setting Pruning fields in the IAVL tree can optimize performance by only writing versions to disk if they are meant to be persisted indefinitely. Versions that are known to be deleted eventually are temporarily held in memory until they are ready to be pruned. This greatly reduces the I/O load of IAVL. - -We can set custom pruning fields in IAVL using: `NewMutableTreeWithOpts` - -## Current design - -### NodeDB -NodeDB has extra fields: - -```go -recentDB dbm.DB // Memory node storage. -recentBatch dbm.Batch // Batched writing buffer for memDB. - -// Pruning fields -keepEvery int64n // Saves version to disk periodically -keepRecent int64 // Saves recent versions in memory -``` - -If version is not going to be persisted to disk, the version is simply saved in `recentDB` (typically a `memDB`) -If version is persisted to disk, the version is written to `recentDB` **and** `snapshotDB` (typically `levelDB`) - -For example, setting keepEvery to 1 and keepRecent to 0 (which is the default setting) will persist every version to snapshot and skip storing -anything in memDB. Setting keepEvery to 10000 and keepRecent to 100 (default Cosmos SDK pruning strategy) is snapshotting every 10000th version and will keep the last 100 versions -in memDB. - -#### Orphans: - -Save orphan to `memDB` under `o|toVersion|fromVersion`. - -If there exists snapshot version `snapVersion` s.t. `fromVersion < snapVersion < toVersion`, save orphan to disk as well under `o|snapVersion|fromVersion`. -NOTE: in unlikely event, that two snapshot versions exist between `fromVersion` and `toVersion`, we use closest snapshot version that is less than `toVersion` - -Can then simply use the old delete algorithm with some minor simplifications/optimizations - -### MutableTree - -MutableTree can be instantiated with a pruning-aware NodeDB. - -When `MutableTree` saves a new Version, it also calls `PruneRecentVersions` on nodeDB which causes oldest version in recentDB (`latestVersion - keepRecent`) to get pruned. diff --git a/docs/tree/mutable_tree.md b/docs/tree/mutable_tree.md index 1e4bec562..86080a1d0 100644 --- a/docs/tree/mutable_tree.md +++ b/docs/tree/mutable_tree.md @@ -375,9 +375,6 @@ Lastly, it returns the tree's hash, the latest version, and nil for error. SaveVersion will error if a tree at the version trying to be saved already exists. -If the IAVL has a custom pruning strategy (`pruningStrategy.keepRecent != 0`), then the nodeDB's `recentDB` must be pruned to ensure that there only exist `keepRecent` versions of the IAVL in the nodeDB. To ensure this, `SaveVersion` will call `PruneRecentVersions` which will return the version numbers which no longer exist in the nodeDB after `recentDB` has been pruned. -`SaveVersion` will then have to update the versions map to set all pruned versions to `false` so that users are aware that the versions are no longer available. - ### DeleteVersion DeleteVersion will simply call nodeDB's `DeleteVersion` function which is documented in the [nodeDB docs](./nodedb.md) and then call `nodeDB.Commit` to flush all batched updates. diff --git a/export_test.go b/export_test.go index 93cc446eb..c20663019 100644 --- a/export_test.go +++ b/export_test.go @@ -271,47 +271,6 @@ func TestExporter_DeleteVersionErrors(t *testing.T) { require.NoError(t, err) } -func TestExporter_PruneVersionIgnores(t *testing.T) { - tree, err := NewMutableTreeWithOpts(db.NewMemDB(), db.NewMemDB(), 0, &Options{ - KeepEvery: 10, - KeepRecent: 2, - }) - require.NoError(t, err) - - tree.Set([]byte("a"), []byte{1}) - _, _, err = tree.SaveVersion() - require.NoError(t, err) - - tree.Set([]byte("b"), []byte{2}) - _, _, err = tree.SaveVersion() - require.NoError(t, err) - - tree.Set([]byte("c"), []byte{3}) - _, _, err = tree.SaveVersion() - require.NoError(t, err) - - require.Equal(t, []int{2, 3}, tree.AvailableVersions()) - - itree, err := tree.GetImmutable(2) - require.NoError(t, err) - exporter := itree.Export() - defer exporter.Close() - - tree.Set([]byte("d"), []byte{4}) - _, _, err = tree.SaveVersion() - require.NoError(t, err) - - require.Equal(t, []int{2, 3, 4}, tree.AvailableVersions()) - - exporter.Close() - tree.Set([]byte("e"), []byte{5}) - _, _, err = tree.SaveVersion() - require.NoError(t, err) - - // FIXME This seems like a bug in the version pruner; version 2 should have been pruned here - require.Equal(t, []int{2, 4, 5}, tree.AvailableVersions()) -} - func BenchmarkExport(b *testing.B) { b.StopTimer() tree := setupExportTreeSized(b, 4096) diff --git a/go.mod b/go.mod index 56424b62b..5ca146d4f 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( github.com/gogo/protobuf v1.3.1 github.com/golang/protobuf v1.4.2 // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/pkg/errors v0.9.1 github.com/stretchr/testify v1.6.1 diff --git a/immutable_tree.go b/immutable_tree.go index 219bfceb7..a8be53b01 100644 --- a/immutable_tree.go +++ b/immutable_tree.go @@ -19,7 +19,7 @@ type ImmutableTree struct { version int64 } -// NewImmutableTree creates both in-memory and persistent instances. +// NewImmutableTree creates both in-memory and persistent instances func NewImmutableTree(db dbm.DB, cacheSize int) *ImmutableTree { if db == nil { // In-memory Tree. @@ -27,18 +27,15 @@ func NewImmutableTree(db dbm.DB, cacheSize int) *ImmutableTree { } return &ImmutableTree{ // NodeDB-backed Tree. - // memDB created but should never be written to - ndb: newNodeDB(db, dbm.NewMemDB(), cacheSize, nil), + ndb: newNodeDB(db, cacheSize, nil), } } -// NewImmutableTreeWithOpts creates ImmutableTree with specified pruning/writing strategy. -// Persists every `keepEvery` version to snapDB and saves last `keepRecent` versions to recentDB -// If sync is true, writes on nodeDB.Commit are blocking -func NewImmutableTreeWithOpts(snapDB dbm.DB, recentDB dbm.DB, cacheSize int, opts *Options) *ImmutableTree { +// NewImmutableTreeWithOpts creates an ImmutableTree with the given options. +func NewImmutableTreeWithOpts(db dbm.DB, cacheSize int, opts *Options) *ImmutableTree { return &ImmutableTree{ // NodeDB-backed Tree. - ndb: newNodeDB(snapDB, recentDB, cacheSize, opts), + ndb: newNodeDB(db, cacheSize, opts), } } diff --git a/import.go b/import.go index 171fa60db..7289b2371 100644 --- a/import.go +++ b/import.go @@ -47,7 +47,7 @@ func newImporter(tree *MutableTree, version int64) (*Importer, error) { return &Importer{ tree: tree, version: version, - batch: tree.ndb.snapshotDB.NewBatch(), + batch: tree.ndb.db.NewBatch(), stack: make([]*Node, 0, 8), }, nil } @@ -133,7 +133,7 @@ func (i *Importer) Add(exportNode *ExportNode) error { return err } i.batch.Close() - i.batch = i.tree.ndb.snapshotDB.NewBatch() + i.batch = i.tree.ndb.db.NewBatch() i.batchSize = 0 } diff --git a/metadata.go b/metadata.go deleted file mode 100644 index c08e79498..000000000 --- a/metadata.go +++ /dev/null @@ -1,18 +0,0 @@ -package iavl - -import ( - "github.com/gogo/protobuf/proto" -) - -// marshal returns Protobuf marshaled bytes of a VersionMetadata object. -func (vm *VersionMetadata) marshal() ([]byte, error) { - return proto.Marshal(vm) -} - -// unmarshalVersionMetadata attempts to decode a VersionMetadata object from Protobuf -// marshaled bytes. -func unmarshalVersionMetadata(bz []byte) (*VersionMetadata, error) { - vm := &VersionMetadata{} - err := proto.Unmarshal(bz, vm) - return vm, err -} diff --git a/metadata_test.go b/metadata_test.go deleted file mode 100644 index 8872acd0b..000000000 --- a/metadata_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package iavl - -import ( - "testing" - "time" - - "github.com/stretchr/testify/require" -) - -func TestVersionMetadata_Serialize(t *testing.T) { - vm := &VersionMetadata{ - Version: 14, - Committed: time.Now().UTC().Unix(), - Updated: time.Now().UTC().Unix(), - RootHash: []byte{0x04, 0x05, 0x00, 0xff, 0x04, 0x05, 0x00, 0xff}, - Snapshot: true, - } - - bz, err := vm.marshal() - require.NoError(t, err) - require.NotNil(t, bz) - - vm2, err := unmarshalVersionMetadata(bz) - require.NoError(t, err) - require.Equal(t, vm.String(), vm2.String()) -} diff --git a/mutable_tree.go b/mutable_tree.go index 5ddaf4a14..524ff4577 100644 --- a/mutable_tree.go +++ b/mutable_tree.go @@ -4,7 +4,6 @@ import ( "bytes" "fmt" "sort" - "time" "github.com/pkg/errors" @@ -26,41 +25,18 @@ type MutableTree struct { *ImmutableTree // The current, working tree. lastSaved *ImmutableTree // The most recently saved tree. orphans map[string]int64 // Nodes removed by changes to working tree. - versions map[int64]bool // The previous versions of the tree saved in disk or memory. + versions map[int64]bool // The previous, saved versions of the tree. ndb *nodeDB } -// NewMutableTree returns a new tree with the specified cache size and datastore, persisting all -// versions to disk. +// NewMutableTree returns a new tree with the specified cache size and datastore. func NewMutableTree(db dbm.DB, cacheSize int) (*MutableTree, error) { - // memDB is initialized but should never be written to - memDB := dbm.NewMemDB() - return NewMutableTreeWithOpts(db, memDB, cacheSize, nil) + return NewMutableTreeWithOpts(db, cacheSize, nil) } -func validateOptions(opts *Options) error { - switch { - case opts == nil: - return nil - case opts.KeepEvery < 0: - return errors.New("keep every cannot be negative") - case opts.KeepRecent < 0: - return errors.New("keep recent cannot be negative") - case opts.KeepRecent == 0 && opts.KeepEvery > 1: - // We cannot snapshot more than every one version when we don't keep any versions in memory. - return errors.New("keep recent cannot be zero when keep every is set larger than one") - } - - return nil -} - -// NewMutableTreeWithOpts returns a new tree with the specified cache size, datastores and options. -func NewMutableTreeWithOpts(snapDB dbm.DB, recentDB dbm.DB, cacheSize int, opts *Options) (*MutableTree, error) { - if err := validateOptions(opts); err != nil { - return nil, err - } - - ndb := newNodeDB(snapDB, recentDB, cacheSize, opts) +// NewMutableTreeWithOpts returns a new tree with the specified options. +func NewMutableTreeWithOpts(db dbm.DB, cacheSize int, opts *Options) (*MutableTree, error) { + ndb := newNodeDB(db, cacheSize, opts) head := &ImmutableTree{ndb: ndb} return &MutableTree{ @@ -78,26 +54,9 @@ func (tree *MutableTree) IsEmpty() bool { return tree.ImmutableTree.Size() == 0 } -// VersionExists returns whether or not a version exists. A version exists if it -// is found in the recentDB or if it exists on disk. +// VersionExists returns whether or not a version exists. func (tree *MutableTree) VersionExists(version int64) bool { - // First, check if the version exists in memory. - if tree.versions[version] { - return true - } - - // Otherwise, verify we have the version flushed to disk. - rootHash, err := tree.ndb.getRoot(version) - if err != nil { - return false - } - - ok, err := tree.ndb.HasSnapshot(rootHash) - if err != nil { - return false - } - - return ok + return tree.versions[version] } // AvailableVersions returns all available versions in ascending order @@ -402,8 +361,7 @@ func (tree *MutableTree) LoadVersion(targetVersion int64) (int64, error) { } // LoadVersionForOverwriting attempts to load a tree at a previously committed -// version. Any versions greater than targetVersion will be deleted along with -// their respective metadata. +// version. Any versions greater than targetVersion will be deleted. func (tree *MutableTree) LoadVersionForOverwriting(targetVersion int64) (int64, error) { latestVersion, err := tree.LoadVersion(targetVersion) if err != nil { @@ -418,8 +376,7 @@ func (tree *MutableTree) LoadVersionForOverwriting(targetVersion int64) (int64, } // GetImmutable loads an ImmutableTree at a given version for querying. The returned tree is -// safe for concurrent access, provided the version is not deleted via `DeleteVersion()` or -// pruning settings. +// safe for concurrent access, provided the version is not deleted, e.g. via `DeleteVersion()`. func (tree *MutableTree) GetImmutable(version int64) (*ImmutableTree, error) { rootHash, err := tree.ndb.getRoot(version) if err != nil { @@ -466,61 +423,10 @@ func (tree *MutableTree) GetVersioned(key []byte, version int64) ( return -1, nil } -// FlushVersion will attempt to manually flush a previously committed version to -// disk. It will return an error if the version does not exist or if the underlying -// flush fails. If the version is already flushed, the method performs a no-op -// and no error is returned. -func (tree *MutableTree) FlushVersion(version int64) error { - rootHash, err := tree.ndb.getRoot(version) - if err != nil { - return err - } - - ok, err := tree.ndb.HasSnapshot(rootHash) - if err != nil { - return err - } - if ok { - return nil - } - - debug("FLUSHING VERSION: %d\n", version) - err = tree.ndb.flushVersion(version) - if err != nil { - return err - } - - vm, err := tree.ndb.GetVersionMetadata(version) - if err != nil { - return err - } - - vm.Snapshot = true - vm.Updated = time.Now().UTC().Unix() - if err := tree.ndb.SetVersionMetadata(vm); err != nil { - return err - } - - // remove flushed version from the recentDB if it's not the latest - if version != tree.version { - if err := tree.ndb.DeleteVersionFromRecent(version, true); err != nil { - return err - } - } - - tree.versions[version] = true - return nil -} - -// SaveVersion saves a new tree version to memDB and removes old version, -// based on the current state of the tree. Returns the hash and new version number. -// If version is snapshot version, persist version to disk as well +// SaveVersion saves a new tree version to disk, based on the current state of +// the tree. Returns the hash and new version number. func (tree *MutableTree) SaveVersion() ([]byte, int64, error) { version := tree.version + 1 - vm := &VersionMetadata{ - Version: version, - Snapshot: tree.ndb.opts.KeepEvery != 0 && version%tree.ndb.opts.KeepEvery == 0, - } if tree.versions[version] { // If the version already exists, return an error as we're attempting to overwrite. @@ -543,32 +449,20 @@ func (tree *MutableTree) SaveVersion() ([]byte, int64, error) { return nil, version, fmt.Errorf("version %d was already saved to different hash %X (existing hash %X)", version, newHash, existingHash) } - tree.versions[version] = true - if tree.root == nil { // There can still be orphans, for example if the root is the node being // removed. debug("SAVE EMPTY TREE %v\n", version) - if err := tree.ndb.SaveOrphans(version, tree.orphans); err != nil { - panic(err) - } - + tree.ndb.SaveOrphans(version, tree.orphans) if err := tree.ndb.SaveEmptyRoot(version); err != nil { - panic(err) + return nil, 0, err } } else { debug("SAVE TREE %v\n", version) - - if _, err := tree.ndb.SaveTree(tree.root, version); err != nil { - panic(err) - } - - if err := tree.ndb.SaveOrphans(version, tree.orphans); err != nil { - panic(err) - } - + tree.ndb.SaveBranch(tree.root) + tree.ndb.SaveOrphans(version, tree.orphans) if err := tree.ndb.SaveRoot(tree.root, version); err != nil { - panic(err) + return nil, 0, err } } @@ -576,10 +470,6 @@ func (tree *MutableTree) SaveVersion() ([]byte, int64, error) { return nil, version, err } - if err := tree.pruneRecentVersion(); err != nil { - return nil, version, err - } - tree.version = version tree.versions[version] = true @@ -588,44 +478,9 @@ func (tree *MutableTree) SaveVersion() ([]byte, int64, error) { tree.lastSaved = tree.ImmutableTree.clone() tree.orphans = map[string]int64{} - // save version metadata - vm.Committed = time.Now().UTC().Unix() - vm.Updated = vm.Committed - vm.RootHash = tree.Hash() - - if err := tree.ndb.SetVersionMetadata(vm); err != nil { - return nil, version, err - } - return tree.Hash(), version, nil } -// pruneRecentVersion looks for a recent version to remove from the recentDB. If -// such a version exists, the corresponding metadata will be updated as well. -func (tree *MutableTree) pruneRecentVersion() error { - prunedVersion, err := tree.ndb.PruneRecentVersion() - if err != nil { - return err - } - - if prunedVersion > 0 { - vm, err := tree.ndb.GetVersionMetadata(prunedVersion) - if err != nil { - return err - } - - vm.Updated = time.Now().UTC().Unix() - if err := tree.ndb.SetVersionMetadata(vm); err != nil { - return err - } - - delete(tree.versions, prunedVersion) - return tree.ndb.Commit() - } - - return nil -} - func (tree *MutableTree) deleteVersion(version int64) error { if version == 0 { return errors.New("version must be greater than 0") @@ -668,16 +523,10 @@ func (tree *MutableTree) DeleteVersions(versions ...int64) error { } // DeleteVersion deletes a tree version from disk. The version can then no -// longer be accessed. Note, the version's metadata will still be retained. In -// addition, it will contain the time at which the version was deleted. +// longer be accessed. func (tree *MutableTree) DeleteVersion(version int64) error { debug("DELETE VERSION: %d\n", version) - vm, err := tree.ndb.GetVersionMetadata(version) - if err != nil { - return err - } - if err := tree.deleteVersion(version); err != nil { return err } @@ -686,13 +535,6 @@ func (tree *MutableTree) DeleteVersion(version int64) error { return err } - // update metadata; snapshot is now false as the version is no longer flushed to disk - vm.Snapshot = false - vm.Updated = time.Now().UTC().Unix() - if err := tree.ndb.SetVersionMetadata(vm); err != nil { - return err - } - delete(tree.versions, version) return nil } @@ -728,10 +570,6 @@ func (tree *MutableTree) deleteVersionsFrom(version int64) error { } } - if err := tree.ndb.DeleteVersionMetadata(version); err != nil { - return err - } - delete(tree.versions, version) } @@ -746,6 +584,7 @@ func (tree *MutableTree) deleteVersionsFrom(version int64) error { } // deleteNodes deletes all nodes which have greater version than current, because they are not useful anymore +// FIXME This should probably happen in NodeDB. func (tree *MutableTree) deleteNodes(version int64, hash []byte) error { if len(hash) == 0 { return nil @@ -764,17 +603,7 @@ func (tree *MutableTree) deleteNodes(version int64, hash []byte) error { } if node.version > version { - vm, err := tree.ndb.GetVersionMetadata(node.version) - if err != nil { - return err - } - - if tree.ndb.isRecentVersion(node.version) { - tree.ndb.recentBatch.Delete(tree.ndb.nodeKey(hash)) - } - if vm.Snapshot { - tree.ndb.snapshotBatch.Delete(tree.ndb.nodeKey(hash)) - } + tree.ndb.batch.Delete(tree.ndb.nodeKey(hash)) } return nil @@ -867,8 +696,8 @@ func (tree *MutableTree) balance(node *Node, orphans *[]*Node) (newSelf *Node) { func (tree *MutableTree) addOrphans(orphans []*Node) { for _, node := range orphans { - if !node.saved { - // We don't need to orphan nodes that were never saved. + if !node.persisted { + // We don't need to orphan nodes that were never persisted. continue } if len(node.hash) == 0 { diff --git a/mutable_tree_test.go b/mutable_tree_test.go index 4e8a64db0..e37c519f7 100644 --- a/mutable_tree_test.go +++ b/mutable_tree_test.go @@ -6,189 +6,11 @@ import ( "runtime" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" db "github.com/tendermint/tm-db" ) -func TestFlushVersion(t *testing.T) { - memDB := db.NewMemDB() - opts := PruningOptions(5, 1) - - tree, err := NewMutableTreeWithOpts(memDB, db.NewMemDB(), 0, opts) - require.NoError(t, err) - require.NotNil(t, tree) - - // set key/value pairs and commit up to KeepEvery - rootHashes := make([][]byte, 0) - for i := int64(0); i < opts.KeepEvery; i++ { - tree.Set([]byte(fmt.Sprintf("key-%d", i)), []byte(fmt.Sprintf("value-%d", i))) - - rh, v, err := tree.SaveVersion() // nolint: govet - require.NoError(t, err) - require.Equal(t, i+1, v) - - rootHashes = append(rootHashes, rh) - } - - // verify the latest version - require.Equal(t, int64(5), tree.Version()) - - // verify we only have the 1st and KeepEvery version flushed to disk - for i, rh := range rootHashes { - version := int64(i + 1) - - ok, err := tree.ndb.HasSnapshot(rh) // nolint: govet - require.NoError(t, err) - - if version == 1 || version%opts.KeepEvery == 0 { - require.True(t, ok) - } else { - require.False(t, ok) - } - } - - // set key/value pairs and commit 2 more times (no flush to disk should occur) - for i := opts.KeepEvery; i < opts.KeepEvery+2; i++ { - tree.set([]byte(fmt.Sprintf("key-%d", i)), []byte(fmt.Sprintf("value-%d", i))) - - rh, v, err := tree.SaveVersion() // nolint: govet - require.NoError(t, err) - require.Equal(t, i+1, v) - - rootHashes = append(rootHashes, rh) - } - - // verify the latest version - require.Equal(t, int64(7), tree.Version()) - - // verify we do not have the latest version flushed to disk - ok, err := tree.ndb.HasSnapshot(rootHashes[len(rootHashes)-1]) - require.NoError(t, err) - require.False(t, ok) - - // verify flushing already flushed version is fine - require.NoError(t, tree.FlushVersion(5)) - - // verify we can flush the latest version - require.NoError(t, tree.FlushVersion(tree.Version())) - - // verify we do have the latest version flushed to disk - ok, err = tree.ndb.HasSnapshot(rootHashes[len(rootHashes)-1]) - require.NoError(t, err) - require.True(t, ok) - - tree2, err := NewMutableTreeWithOpts(memDB, db.NewMemDB(), 0, opts) - require.NoError(t, err) - require.NotNil(t, tree2) - - // verify we can load the previously manually flushed version on a new tree - // and fetch all keys and values - v, err := tree2.LoadVersion(tree.Version()) - require.NoError(t, err) - require.Equal(t, tree.Version(), v) - - for i := int64(0); i < v; i++ { - _, value := tree2.Get([]byte(fmt.Sprintf("key-%d", i))) - assert.Equal(t, []byte(fmt.Sprintf("value-%d", i)), value) - } - - // also verify that we can load the automatically flushed version and fetch - // all keys and values, and that no subsequent keys are present. - v, err = tree2.LoadVersion(5) - require.NoError(t, err) - require.EqualValues(t, 5, v) - - for i := int64(0); i < v+10; i++ { - _, value := tree2.Get([]byte(fmt.Sprintf("key-%d", i))) - if i < v { - assert.Equal(t, []byte(fmt.Sprintf("value-%d", i)), value) - } else { - assert.Nil(t, value) - } - } -} - -func TestFlushVersion_SetGet(t *testing.T) { - memDB := db.NewMemDB() - opts := PruningOptions(5, 1) - - tree, err := NewMutableTreeWithOpts(memDB, db.NewMemDB(), 0, opts) - require.NoError(t, err) - require.NotNil(t, tree) - - tree.Set([]byte("a"), []byte{1}) - tree.Set([]byte("b"), []byte{2}) - _, version, err := tree.SaveVersion() - require.NoError(t, err) - - err = tree.FlushVersion(version) - require.NoError(t, err) - - tree, err = NewMutableTreeWithOpts(memDB, db.NewMemDB(), 0, opts) - require.NoError(t, err) - _, err = tree.LoadVersion(version) - require.NoError(t, err) - - _, value := tree.Get([]byte("a")) - assert.Equal(t, []byte{1}, value) - _, value = tree.Get([]byte("b")) - assert.Equal(t, []byte{2}, value) -} - -func TestFlushVersion_Missing(t *testing.T) { - tree, err := NewMutableTreeWithOpts(db.NewMemDB(), db.NewMemDB(), 0, PruningOptions(5, 1)) - require.NoError(t, err) - require.NotNil(t, tree) - - tree.Set([]byte("a"), []byte{1}) - tree.Set([]byte("b"), []byte{2}) - _, _, err = tree.SaveVersion() - require.NoError(t, err) - - err = tree.FlushVersion(2) - require.Error(t, err) -} - -func TestFlushVersion_Empty(t *testing.T) { - memDB := db.NewMemDB() - opts := PruningOptions(5, 1) - tree, err := NewMutableTreeWithOpts(memDB, db.NewMemDB(), 0, opts) - require.NoError(t, err) - require.NotNil(t, tree) - - // save a couple of versions - _, version, err := tree.SaveVersion() - require.NoError(t, err) - assert.EqualValues(t, 1, version) - - _, version, err = tree.SaveVersion() - require.NoError(t, err) - assert.EqualValues(t, 2, version) - - // flush the latest version - err = tree.FlushVersion(2) - require.NoError(t, err) - - // try to load the tree in a new memDB - tree, err = NewMutableTreeWithOpts(memDB, db.NewMemDB(), 0, opts) - require.NoError(t, err) - require.NotNil(t, tree) - - version, err = tree.LoadVersion(2) - require.NoError(t, err) - assert.EqualValues(t, 2, version) - - // loading the first version should fail - tree, err = NewMutableTreeWithOpts(memDB, db.NewMemDB(), 0, opts) - require.NoError(t, err) - require.NotNil(t, tree) - - _, err = tree.LoadVersion(1) - require.Error(t, err) -} - func TestDelete(t *testing.T) { memDB := db.NewMemDB() tree, err := NewMutableTree(memDB, 0) @@ -283,26 +105,6 @@ func TestMutableTree_DeleteVersions(t *testing.T) { } } -func TestEmptyRecents(t *testing.T) { - memDB := db.NewMemDB() - opts := Options{ - KeepRecent: 100, - KeepEvery: 10000, - } - - tree, err := NewMutableTreeWithOpts(memDB, db.NewMemDB(), 0, &opts) - require.NoError(t, err) - hash, version, err := tree.SaveVersion() - - require.Nil(t, err) - require.Equal(t, int64(1), version) - require.Nil(t, hash) - require.True(t, tree.VersionExists(int64(1))) - - _, err = tree.GetImmutable(int64(1)) - require.NoError(t, err) -} - func BenchmarkMutableTree_Set(b *testing.B) { db := db.NewDB("test", db.MemDBBackend, "") t, err := NewMutableTree(db, 100000) diff --git a/node.go b/node.go index 07a526ef9..6993b37aa 100644 --- a/node.go +++ b/node.go @@ -25,8 +25,7 @@ type Node struct { leftNode *Node rightNode *Node height int8 - saved bool // saved to memory or disk - persisted bool // persisted to disk + persisted bool } // NewNode returns a new node from a key, value and version. diff --git a/nodedb.go b/nodedb.go index b28065975..c613b5bc1 100644 --- a/nodedb.go +++ b/nodedb.go @@ -8,7 +8,6 @@ import ( "sort" "sync" - lru "github.com/hashicorp/golang-lru" "github.com/pkg/errors" dbm "github.com/tendermint/tm-db" ) @@ -32,130 +31,37 @@ var ( // Root nodes are indexed separately by their version rootKeyFormat = NewKeyFormat('r', int64Size) // r - - metadataKeyFormat = NewKeyFormat('m', int64Size) // m ) type nodeDB struct { mtx sync.Mutex // Read/write lock. - snapshotDB dbm.DB // Persistent node storage. - recentDB dbm.DB // Memory node storage. - snapshotBatch dbm.Batch // Batched writing buffer. - recentBatch dbm.Batch // Batched writing buffer for recentDB. + db dbm.DB // Persistent node storage. + batch dbm.Batch // Batched writing buffer. opts *Options // Options to customize for pruning/writing - versionReaders map[int64]uint32 // Number of active version readers (prevents pruning) + versionReaders map[int64]uint32 // Number of active version readers latestVersion int64 nodeCache map[string]*list.Element // Node cache. nodeCacheSize int // Node cache size limit in elements. nodeCacheQueue *list.List // LRU queue of cache elements. Used for deletion. - - vmCache *lru.Cache // LRU cache of version metadata } -func newNodeDB(snapshotDB dbm.DB, recentDB dbm.DB, cacheSize int, opts *Options) *nodeDB { +func newNodeDB(db dbm.DB, cacheSize int, opts *Options) *nodeDB { if opts == nil { opts = DefaultOptions() } - - vmCache, err := lru.New(20000) - if err != nil { - panic(fmt.Errorf("failed to create metadata cache: %w", err)) - } - return &nodeDB{ - snapshotDB: snapshotDB, - recentDB: recentDB, - snapshotBatch: snapshotDB.NewBatch(), - recentBatch: recentDB.NewBatch(), + db: db, + batch: db.NewBatch(), opts: opts, latestVersion: 0, // initially invalid nodeCache: make(map[string]*list.Element), nodeCacheSize: cacheSize, nodeCacheQueue: list.New(), versionReaders: make(map[int64]uint32, 8), - vmCache: vmCache, } } -// GetVersionMetadata returns a reference to VersionMetadata for a given version. -// The object is first checked against the internal VersionMetadata LRU cache. If -// it does not exist, it will be queried for via the snapshotDB and added to the -// LRU cache. An error is returned if the VersionMetadata cannot be decoded or -// queried for in the snapshotDB. -func (ndb *nodeDB) GetVersionMetadata(version int64) (*VersionMetadata, error) { - key := metadataKeyFormat.Key(version) - - v, ok := ndb.vmCache.Get(string(key)) - if ok { - return v.(*VersionMetadata), nil - } - - bz, err := ndb.snapshotDB.Get(key) - if err != nil { - return nil, err - } - - // In cases where version metadata does not exist, say for a non-empty tree, - // we construct metadata on the fly. This is for backwards compatibility WRT - // to snapshotting. - if len(bz) == 0 { - return &VersionMetadata{ - Version: version, - Snapshot: ndb.opts.KeepEvery != 0 && version%ndb.opts.KeepEvery == 0, - }, nil - } - - vm, err := unmarshalVersionMetadata(bz) - if err != nil { - return nil, err - } - - _ = ndb.vmCache.Add(string(key), vm) - return vm, nil -} - -// SetVersionMetadata saves a VersionMetadata in the snapshotDB and performs a -// write-through to the VersionMetadata LRU cache. It returns an error if encoding -// or saving to the snapshotDB fails. -func (ndb *nodeDB) SetVersionMetadata(vm *VersionMetadata) error { - key := metadataKeyFormat.Key(vm.Version) - - bz, err := vm.marshal() - if err != nil { - return err - } - - if err := ndb.snapshotDB.Set(key, bz); err != nil { - return err - } - - _ = ndb.vmCache.Add(string(key), vm) - return nil -} - -// DeleteVersionMetadata removes a VersionMetadata object from the snapshotDB -// and the LRU cache. -func (ndb *nodeDB) DeleteVersionMetadata(version int64) error { - vm, err := ndb.GetVersionMetadata(version) - if err != nil { - return err - } - - key := metadataKeyFormat.Key(vm.Version) - - if err := ndb.snapshotDB.Delete(key); err != nil { - return err - } - - ndb.vmCache.Remove(string(key)) - return nil -} - -func (ndb *nodeDB) isRecentVersion(version int64) bool { - return ndb.opts.KeepRecent != 0 && version > ndb.latestVersion-ndb.opts.KeepRecent -} - // GetNode gets a node from memory or disk. If it is an inner node, it does not // load its children. func (ndb *nodeDB) GetNode(hash []byte) *Node { @@ -174,51 +80,34 @@ func (ndb *nodeDB) GetNode(hash []byte) *Node { } // Doesn't exist, load. - buf, err := ndb.recentDB.Get(ndb.nodeKey(hash)) + buf, err := ndb.db.Get(ndb.nodeKey(hash)) if err != nil { panic(fmt.Sprintf("can't get node %X: %v", hash, err)) } - persisted := false if buf == nil { - // Doesn't exist, load from disk - buf, err = ndb.snapshotDB.Get(ndb.nodeKey(hash)) - if err != nil { - panic(err) - } - if buf == nil { - panic(fmt.Sprintf("Value missing for hash %x corresponding to nodeKey %x", hash, ndb.nodeKey(hash))) - } - persisted = true + panic(fmt.Sprintf("Value missing for hash %x corresponding to nodeKey %x", hash, ndb.nodeKey(hash))) } node, err := MakeNode(buf) if err != nil { panic(fmt.Sprintf("Error reading Node. bytes: %x, error: %v", buf, err)) } - node.saved = true - node.persisted = persisted node.hash = hash + node.persisted = true ndb.cacheNode(node) return node } // SaveNode saves a node to disk. -func (ndb *nodeDB) SaveNode(node *Node, flushToDisk bool) { - ndb.saveNodeBatch(node, flushToDisk, ndb.recentBatch, ndb.snapshotBatch) -} - -func (ndb *nodeDB) saveNodeBatch(node *Node, flushToDisk bool, rb, sb dbm.Batch) { +func (ndb *nodeDB) SaveNode(node *Node) { ndb.mtx.Lock() defer ndb.mtx.Unlock() if node.hash == nil { panic("Expected to find node.hash, but none found.") } - if node.saved && !flushToDisk { - return - } if node.persisted { panic("Shouldn't be calling save on an already persisted node.") } @@ -231,98 +120,49 @@ func (ndb *nodeDB) saveNodeBatch(node *Node, flushToDisk bool, rb, sb dbm.Batch) panic(err) } - if !node.saved { - node.saved = true - rb.Set(ndb.nodeKey(node.hash), buf.Bytes()) - } - - if flushToDisk { - sb.Set(ndb.nodeKey(node.hash), buf.Bytes()) - node.persisted = true - node.saved = true - } + ndb.batch.Set(ndb.nodeKey(node.hash), buf.Bytes()) + debug("BATCH SAVE %X %p\n", node.hash, node) + node.persisted = true + ndb.cacheNode(node) } -// Has checks if a hash exists in the recentDB or the snapshotDB. +// Has checks if a hash exists in the database. func (ndb *nodeDB) Has(hash []byte) (bool, error) { key := ndb.nodeKey(hash) - val, err := ndb.recentDB.Get(key) - if err != nil { - return false, errors.Wrap(err, "recentDB") - } - if val != nil { - return true, nil - } - - return ndb.HasSnapshot(hash) -} - -// HasSnapshot returns true if a given hash exists in the snapshotDB. -func (ndb *nodeDB) HasSnapshot(hash []byte) (bool, error) { - key := ndb.nodeKey(hash) - if ldb, ok := ndb.snapshotDB.(*dbm.GoLevelDB); ok { - var exists bool - + if ldb, ok := ndb.db.(*dbm.GoLevelDB); ok { exists, err := ldb.DB().Has(key, nil) if err != nil { - return false, errors.Wrap(err, "snapshotDB") + return false, err } - return exists, nil } - - value, err := ndb.snapshotDB.Get(key) + value, err := ndb.db.Get(key) if err != nil { - return false, errors.Wrap(err, "snapshotDB") + return false, err } return value != nil, nil } -// SaveTree takes a rootNode and version. Saves all nodes in tree using SaveBranch -func (ndb *nodeDB) SaveTree(root *Node, version int64) ([]byte, error) { - vm, err := ndb.GetVersionMetadata(version) - if err != nil { - return nil, err - } - - return ndb.SaveBranch(root, vm.Snapshot), nil -} - // SaveBranch saves the given node and all of its descendants. // NOTE: This function clears leftNode/rigthNode recursively and // calls _hash() on the given node. // TODO refactor, maybe use hashWithCount() but provide a callback. -func (ndb *nodeDB) SaveBranch(node *Node, flushToDisk bool) []byte { - return ndb.saveBranchBatch(node, flushToDisk, ndb.recentBatch, ndb.snapshotBatch) -} - -// TODO: Reconsider design to not have batch objects be fields of a nodeDB type. -// Instead, batch objects should be created when needed as passed as arguments -// where needed. This allows the IO flow to be easier to reason about and impproves -// testability. -func (ndb *nodeDB) saveBranchBatch(node *Node, flushToDisk bool, rb, sb dbm.Batch) []byte { - if node.saved && !flushToDisk { - return node.hash - } +func (ndb *nodeDB) SaveBranch(node *Node) []byte { if node.persisted { return node.hash } - if node.size != 1 { - if node.leftNode == nil { - node.leftNode = ndb.GetNode(node.leftHash) - } - if node.rightNode == nil { - node.rightNode = ndb.GetNode(node.rightHash) - } - node.leftHash = ndb.saveBranchBatch(node.leftNode, flushToDisk, rb, sb) - node.rightHash = ndb.saveBranchBatch(node.rightNode, flushToDisk, rb, sb) + if node.leftNode != nil { + node.leftHash = ndb.SaveBranch(node.leftNode) + } + if node.rightNode != nil { + node.rightHash = ndb.SaveBranch(node.rightNode) } node._hash() - ndb.saveNodeBatch(node, flushToDisk, rb, sb) + ndb.SaveNode(node) node.leftNode = nil node.rightNode = nil @@ -334,173 +174,73 @@ func (ndb *nodeDB) saveBranchBatch(node *Node, flushToDisk bool, rb, sb dbm.Batc func (ndb *nodeDB) DeleteVersion(version int64, checkLatestVersion bool) error { ndb.mtx.Lock() defer ndb.mtx.Unlock() - return ndb.deleteVersion(version, checkLatestVersion, false) -} - -func (ndb *nodeDB) DeleteVersionFromRecent(version int64, checkLatestVersion bool) error { - ndb.mtx.Lock() - defer ndb.mtx.Unlock() - return ndb.deleteVersion(version, checkLatestVersion, true) -} -func (ndb *nodeDB) deleteVersion(version int64, checkLatestVersion, memOnly bool) error { if ndb.versionReaders[version] > 0 { return errors.Errorf("unable to delete version %v, it has %v active readers", version, ndb.versionReaders[version]) } - vm, err := ndb.GetVersionMetadata(version) - if err != nil { - return err - } - - if err := ndb.deleteOrphans(version, memOnly, vm.Snapshot); err != nil { - return err - } - - ndb.deleteRoot(version, checkLatestVersion, memOnly, vm.Snapshot) + ndb.deleteOrphans(version) + ndb.deleteRoot(version, checkLatestVersion) return nil } // Saves orphaned nodes to disk under a special prefix. // version: the new version being saved. // orphans: the orphan nodes created since version-1 -func (ndb *nodeDB) SaveOrphans(version int64, orphans map[string]int64) error { +func (ndb *nodeDB) SaveOrphans(version int64, orphans map[string]int64) { ndb.mtx.Lock() defer ndb.mtx.Unlock() toVersion := ndb.getPreviousVersion(version) - for hash, fromVersion := range orphans { - var flushToDisk bool - - vm, err := ndb.GetVersionMetadata(fromVersion) - if err != nil { - return err - } - - if ndb.opts.KeepEvery != 0 { - // if snapshot version in between fromVersion and toVersion INCLUSIVE, then flush to disk. - flushToDisk = fromVersion/ndb.opts.KeepEvery != toVersion/ndb.opts.KeepEvery || vm.Snapshot - } - - debug("SAVEORPHAN %v-%v %X flushToDisk: %t\n", fromVersion, toVersion, hash, flushToDisk) - ndb.saveOrphan([]byte(hash), fromVersion, toVersion, flushToDisk) + debug("SAVEORPHAN %v-%v %X\n", fromVersion, toVersion, hash) + ndb.saveOrphan([]byte(hash), fromVersion, toVersion) } - - return nil } -// Saves a single orphan to recentDB. If flushToDisk, persist to disk as well. -func (ndb *nodeDB) saveOrphan(hash []byte, fromVersion, toVersion int64, flushToDisk bool) { +// Saves a single orphan to disk. +func (ndb *nodeDB) saveOrphan(hash []byte, fromVersion, toVersion int64) { if fromVersion > toVersion { panic(fmt.Sprintf("Orphan expires before it comes alive. %d > %d", fromVersion, toVersion)) } - - if ndb.isRecentVersion(toVersion) { - key := ndb.orphanKey(fromVersion, toVersion, hash) - ndb.recentBatch.Set(key, hash) - } - - if flushToDisk { - // save to disk with toVersion equal to snapshotVersion closest to original toVersion - snapVersion := toVersion - (toVersion % ndb.opts.KeepEvery) - key := ndb.orphanKey(fromVersion, snapVersion, hash) - ndb.snapshotBatch.Set(key, hash) - } + key := ndb.orphanKey(fromVersion, toVersion, hash) + ndb.batch.Set(key, hash) } // deleteOrphans deletes orphaned nodes from disk, and the associated orphan // entries. -func (ndb *nodeDB) deleteOrphans(version int64, memOnly, isSnapshot bool) error { - if ndb.opts.KeepRecent != 0 { - ndb.deleteOrphansMem(version) - } +func (ndb *nodeDB) deleteOrphans(version int64) { + // Will be zero if there is no previous version. + predecessor := ndb.getPreviousVersion(version) - if isSnapshot && !memOnly { - predecessor := getPreviousVersionFromDB(version, ndb.snapshotDB) - traverseOrphansVersionFromDB(ndb.snapshotDB, version, func(key, hash []byte) { - ndb.snapshotBatch.Delete(key) - ndb.deleteOrphansHelper(ndb.snapshotDB, ndb.snapshotBatch, true, predecessor, key, hash) - }) - } + // Traverse orphans with a lifetime ending at the version specified. + // TODO optimize. + ndb.traverseOrphansVersion(version, func(key, hash []byte) { + var fromVersion, toVersion int64 - return nil -} + // See comment on `orphanKeyFmt`. Note that here, `version` and + // `toVersion` are always equal. + orphanKeyFormat.Scan(key, &toVersion, &fromVersion) -func (ndb *nodeDB) deleteOrphansMem(version int64) { - traverseOrphansVersionFromDB(ndb.recentDB, version, func(key, hash []byte) { - if ndb.opts.KeepRecent == 0 { - return - } - ndb.recentBatch.Delete(key) - // common case, we are deleting orphans from least recent version that is getting pruned from memDB - if version == ndb.latestVersion-ndb.opts.KeepRecent { - // delete orphan look-up, delete and uncache node - ndb.recentBatch.Delete(ndb.nodeKey(hash)) + // Delete orphan key and reverse-lookup key. + ndb.batch.Delete(key) + + // If there is no predecessor, or the predecessor is earlier than the + // beginning of the lifetime (ie: negative lifetime), or the lifetime + // spans a single version and that version is the one being deleted, we + // can delete the orphan. Otherwise, we shorten its lifetime, by + // moving its endpoint to the previous version. + if predecessor < fromVersion || fromVersion == toVersion { + debug("DELETE predecessor:%v fromVersion:%v toVersion:%v %X\n", predecessor, fromVersion, toVersion, hash) + ndb.batch.Delete(ndb.nodeKey(hash)) ndb.uncacheNode(hash) - return + } else { + debug("MOVE predecessor:%v fromVersion:%v toVersion:%v %X\n", predecessor, fromVersion, toVersion, hash) + ndb.saveOrphan(hash, fromVersion, predecessor) } - - predecessor := getPreviousVersionFromDB(version, ndb.recentDB) - - // user is manually deleting version from memDB - // thus predecessor may exist in memDB - // Will be zero if there is no previous version. - ndb.deleteOrphansHelper(ndb.recentDB, ndb.recentBatch, false, predecessor, key, hash) }) } -func (ndb *nodeDB) deleteOrphansHelper(db dbm.DB, batch dbm.Batch, flushToDisk bool, predecessor int64, key, hash []byte) { - var fromVersion, toVersion int64 - - // See comment on `orphanKeyFmt`. Note that here, `version` and - // `toVersion` are always equal. - orphanKeyFormat.Scan(key, &toVersion, &fromVersion) - - // If there is no predecessor, or the predecessor is earlier than the - // beginning of the lifetime (ie: negative lifetime), or the lifetime - // spans a single version and that version is the one being deleted, we - // can delete the orphan. Otherwise, we shorten its lifetime, by - // moving its endpoint to the previous version. - if predecessor < fromVersion || fromVersion == toVersion { - debug("DELETE predecessor:%v fromVersion:%v toVersion:%v %X flushToDisk: %t\n", predecessor, fromVersion, toVersion, hash, flushToDisk) - batch.Delete(ndb.nodeKey(hash)) - ndb.uncacheNode(hash) - } else { - debug("MOVE predecessor:%v fromVersion:%v toVersion:%v %X flushToDisk: %t\n", predecessor, fromVersion, toVersion, hash, flushToDisk) - ndb.saveOrphan(hash, fromVersion, predecessor, flushToDisk) - } -} - -func (ndb *nodeDB) PruneRecentVersion() (int64, error) { - if ndb.opts.KeepRecent == 0 || ndb.latestVersion-ndb.opts.KeepRecent <= 0 { - return 0, nil - } - - ndb.mtx.Lock() - defer ndb.mtx.Unlock() - - pruneVer := ndb.latestVersion - ndb.opts.KeepRecent - if ndb.versionReaders[pruneVer] > 0 { - return 0, nil - } - - if err := ndb.deleteVersion(pruneVer, true, true); err != nil { - return 0, err - } - - vm, err := ndb.GetVersionMetadata(pruneVer) - if err != nil { - return 0, err - } - - if vm.Snapshot { - return 0, nil - } - - return pruneVer, nil -} - func (ndb *nodeDB) nodeKey(hash []byte) []byte { return nodeKeyFormat.KeyBytes(hash) } @@ -531,18 +271,7 @@ func (ndb *nodeDB) resetLatestVersion(version int64) { } func (ndb *nodeDB) getPreviousVersion(version int64) int64 { - // If version exists in recentDB, check recentDB for any previous version - if ndb.isRecentVersion(version) { - prev := getPreviousVersionFromDB(version, ndb.recentDB) - if prev != 0 { - return prev - } - } - return getPreviousVersionFromDB(version, ndb.snapshotDB) -} - -func getPreviousVersionFromDB(version int64, db dbm.DB) int64 { - itr, err := db.ReverseIterator( + itr, err := ndb.db.ReverseIterator( rootKeyFormat.Key(1), rootKeyFormat.Key(version), ) @@ -562,71 +291,25 @@ func getPreviousVersionFromDB(version int64, db dbm.DB) int64 { } // deleteRoot deletes the root entry from disk, but not the node it points to. -func (ndb *nodeDB) deleteRoot(version int64, checkLatestVersion, memOnly, isSnapshot bool) { +func (ndb *nodeDB) deleteRoot(version int64, checkLatestVersion bool) { if checkLatestVersion && version == ndb.getLatestVersion() { panic("Tried to delete latest version") } - - key := ndb.rootKey(version) - ndb.recentBatch.Delete(key) - - if isSnapshot && !memOnly { - ndb.snapshotBatch.Delete(key) - } + ndb.batch.Delete(ndb.rootKey(version)) } func (ndb *nodeDB) traverseOrphans(fn func(k, v []byte)) { ndb.traversePrefix(orphanKeyFormat.Key(), fn) } -func traverseOrphansFromDB(db dbm.DB, fn func(k, v []byte)) { - traversePrefixFromDB(db, orphanKeyFormat.Key(), fn) -} - // Traverse orphans ending at a certain version. -// NOTE: If orphan is in recentDB and levelDB (version > latestVersion-keepRecent && version%keepEvery == 0) -// traverse will return the node twice. -// func (ndb *nodeDB) traverseOrphansVersion(version int64, fn func(k, v []byte)) { -// prefix := orphanKeyFormat.Key(version) -// if ndb.isRecentVersion(version) { -// traversePrefixFromDB(ndb.recentDB, prefix, fn) -// } -// if ndb.isSnapshotVersion(version) { -// traversePrefixFromDB(ndb.snapshotDB, prefix, fn) -// } -// } - -func traverseOrphansVersionFromDB(db dbm.DB, version int64, fn func(k, v []byte)) { - prefix := orphanKeyFormat.Key(version) - traversePrefixFromDB(db, prefix, fn) -} - -// Traverse all keys from recentDB and disk DB -func (ndb *nodeDB) traverse(fn func(key, value []byte)) { - memItr, err := ndb.recentDB.Iterator(nil, nil) - if err != nil { - panic(err) - } - defer memItr.Close() - - for ; memItr.Valid(); memItr.Next() { - fn(memItr.Key(), memItr.Value()) - } - - itr, err := ndb.snapshotDB.Iterator(nil, nil) - if err != nil { - panic(err) - } - defer itr.Close() - - for ; itr.Valid(); itr.Next() { - fn(itr.Key(), itr.Value()) - } +func (ndb *nodeDB) traverseOrphansVersion(version int64, fn func(k, v []byte)) { + ndb.traversePrefix(orphanKeyFormat.Key(version), fn) } -// Traverse all keys from provided DB -func traverseFromDB(db dbm.DB, fn func(key, value []byte)) { - itr, err := db.Iterator(nil, nil) +// Traverse all keys. +func (ndb *nodeDB) traverse(fn func(key, value []byte)) { + itr, err := ndb.db.Iterator(nil, nil) if err != nil { panic(err) } @@ -637,32 +320,9 @@ func traverseFromDB(db dbm.DB, fn func(key, value []byte)) { } } -// Traverse all keys with a certain prefix from recentDB and disk DB +// Traverse all keys with a certain prefix. func (ndb *nodeDB) traversePrefix(prefix []byte, fn func(k, v []byte)) { - memItr, err := dbm.IteratePrefix(ndb.recentDB, prefix) - if err != nil { - panic(err) - } - defer memItr.Close() - - for ; memItr.Valid(); memItr.Next() { - fn(memItr.Key(), memItr.Value()) - } - - itr, err := dbm.IteratePrefix(ndb.snapshotDB, prefix) - if err != nil { - panic(err) - } - defer itr.Close() - - for ; itr.Valid(); itr.Next() { - fn(itr.Key(), itr.Value()) - } -} - -// Traverse all keys with a certain prefix from given DB -func traversePrefixFromDB(db dbm.DB, prefix []byte, fn func(k, v []byte)) { - itr, err := dbm.IteratePrefix(db, prefix) + itr, err := dbm.IteratePrefix(ndb.db, prefix) if err != nil { panic(err) } @@ -693,63 +353,29 @@ func (ndb *nodeDB) cacheNode(node *Node) { } } -// Write to disk and memDB +// Write to disk. func (ndb *nodeDB) Commit() error { ndb.mtx.Lock() defer ndb.mtx.Unlock() var err error - if ndb.opts.KeepEvery != 0 { - if ndb.opts.Sync { - err = ndb.snapshotBatch.WriteSync() - if err != nil { - return errors.Wrap(err, "error in snapShotBatch writesync") - } - } else { - err = ndb.snapshotBatch.Write() - if err != nil { - return errors.Wrap(err, "error in snapShotBatch write") - } - } - - ndb.snapshotBatch.Close() + if ndb.opts.Sync { + err = ndb.batch.WriteSync() + } else { + err = ndb.batch.Write() } - - if ndb.opts.KeepRecent != 0 { - if ndb.opts.Sync { - err = ndb.recentBatch.WriteSync() - if err != nil { - return errors.Wrap(err, "error in recentBatch writesync") - } - } else { - err = ndb.recentBatch.Write() - if err != nil { - return errors.Wrap(err, "error in recentBatch write") - } - } - - ndb.recentBatch.Close() + if err != nil { + return errors.Wrap(err, "failed to write batch") } - ndb.snapshotBatch = ndb.snapshotDB.NewBatch() - ndb.recentBatch = ndb.recentDB.NewBatch() + ndb.batch.Close() + ndb.batch = ndb.db.NewBatch() return nil } func (ndb *nodeDB) getRoot(version int64) ([]byte, error) { - if ndb.isRecentVersion(version) { - memroot, err := ndb.recentDB.Get(ndb.rootKey(version)) - if err != nil { - return nil, err - } - // TODO: maybe I shouldn't check in snapshot if it isn't here - if memroot != nil { - return memroot, nil - } - } - - return ndb.snapshotDB.Get(ndb.rootKey(version)) + return ndb.db.Get(ndb.rootKey(version)) } func (ndb *nodeDB) getRoots() (map[int64][]byte, error) { @@ -769,26 +395,15 @@ func (ndb *nodeDB) SaveRoot(root *Node, version int64) error { if len(root.hash) == 0 { panic("SaveRoot: root hash should not be empty") } - - vm, err := ndb.GetVersionMetadata(version) - if err != nil { - return err - } - - return ndb.saveRoot(root.hash, version, vm.Snapshot) + return ndb.saveRoot(root.hash, version) } // SaveEmptyRoot creates an entry on disk for an empty root. func (ndb *nodeDB) SaveEmptyRoot(version int64) error { - vm, err := ndb.GetVersionMetadata(version) - if err != nil { - return err - } - - return ndb.saveRoot([]byte{}, version, vm.Snapshot) + return ndb.saveRoot([]byte{}, version) } -func (ndb *nodeDB) saveRoot(hash []byte, version int64, flushToDisk bool) error { +func (ndb *nodeDB) saveRoot(hash []byte, version int64) error { ndb.mtx.Lock() defer ndb.mtx.Unlock() @@ -796,25 +411,11 @@ func (ndb *nodeDB) saveRoot(hash []byte, version int64, flushToDisk bool) error return fmt.Errorf("must save consecutive versions; expected %d, got %d", ndb.getLatestVersion()+1, version) } - key := ndb.rootKey(version) + ndb.batch.Set(ndb.rootKey(version), hash) ndb.updateLatestVersion(version) - ndb.recentBatch.Set(key, hash) - if flushToDisk { - ndb.snapshotBatch.Set(key, hash) - } - return nil } -func (ndb *nodeDB) saveRootBatch(hash []byte, version int64, rb, sb dbm.Batch) { - ndb.mtx.Lock() - defer ndb.mtx.Unlock() - - key := ndb.rootKey(version) - rb.Set(key, hash) - sb.Set(key, hash) -} - func (ndb *nodeDB) incrVersionReaders(version int64) { ndb.mtx.Lock() defer ndb.mtx.Unlock() @@ -829,52 +430,6 @@ func (ndb *nodeDB) decrVersionReaders(version int64) { } } -func (ndb *nodeDB) flushVersion(version int64) error { - // Create new recentDB and snapshotDB batch objects. We don't use the current - // batch objects of the nodeDB as we don't want to write state prematurely or - // have conflicting state. - // - // NOTE: We ignore any write that happen to recentBatch. - rb := ndb.recentDB.NewBatch() - defer rb.Close() - - sb := ndb.snapshotDB.NewBatch() - defer sb.Close() - - rootHash, err := ndb.getRoot(version) - if err != nil { - return err - } - - switch { - case rootHash == nil: - return fmt.Errorf("version %v does not exist in recentDB", version) - - case len(rootHash) == 0: - ndb.saveRootBatch([]byte{}, version, rb, sb) - - default: - // save branch, the root, and the necessary orphans - node := ndb.GetNode(rootHash) - ndb.saveBranchBatch(node, true, rb, sb) - ndb.saveRootBatch(node.hash, version, rb, sb) - } - - traverseOrphansVersionFromDB(ndb.recentDB, version, func(k, v []byte) { - var fromVersion, toVersion int64 - orphanKeyFormat.Scan(k, &toVersion, &fromVersion) - - ndb.saveOrphan(v, fromVersion, toVersion, true) - }) - - if err := sb.WriteSync(); err != nil { - return fmt.Errorf("failed to write (sync) the snapshot batch: %w", err) - } - - // NOTE: We do not need to write/flush the recent batch. - return nil -} - ////////////////// Utility and test functions ///////////////////////////////// func (ndb *nodeDB) leafNodes() []*Node { @@ -897,15 +452,6 @@ func (ndb *nodeDB) nodes() []*Node { return nodes } -func (ndb *nodeDB) nodesFromDB(db dbm.DB) []*Node { - nodes := []*Node{} - - ndb.traverseNodesFromDB(db, func(hash []byte, node *Node) { - nodes = append(nodes, node) - }) - return nodes -} - func (ndb *nodeDB) orphans() [][]byte { orphans := [][]byte{} @@ -954,36 +500,11 @@ func (ndb *nodeDB) traverseNodes(fn func(hash []byte, node *Node)) { // restoreNodes restores nodes, which was orphaned, but after overwriting should not be orphans anymore func (ndb *nodeDB) restoreNodes(version int64) { - traverseOrphansVersionFromDB(ndb.recentDB, version, func(key, hash []byte) { - // Delete orphan key and reverse-lookup key. - ndb.recentBatch.Delete(key) - }) - - traverseOrphansVersionFromDB(ndb.snapshotDB, version, func(key, hash []byte) { - // Delete orphan key and reverse-lookup key. - ndb.snapshotBatch.Delete(key) - }) -} - -func (ndb *nodeDB) traverseNodesFromDB(db dbm.DB, fn func(hash []byte, node *Node)) { - nodes := []*Node{} - - traversePrefixFromDB(db, nodeKeyFormat.Key(), func(key, value []byte) { - node, err := MakeNode(value) - if err != nil { - panic(fmt.Sprintf("Couldn't decode node from database: %v", err)) - } - nodeKeyFormat.Scan(key, &node.hash) - nodes = append(nodes, node) + // FIXME This fails to take into account future orphans, see: + // https://github.com/cosmos/iavl/issues/273 + ndb.traverseOrphansVersion(version, func(key, hash []byte) { + ndb.batch.Delete(key) }) - - sort.Slice(nodes, func(i, j int) bool { - return bytes.Compare(nodes[i].key, nodes[j].key) < 0 - }) - - for _, n := range nodes { - fn(n.hash, n) - } } func (ndb *nodeDB) String() string { diff --git a/nodedb_test.go b/nodedb_test.go index 30a4bb0da..8e891a15e 100644 --- a/nodedb_test.go +++ b/nodedb_test.go @@ -2,12 +2,8 @@ package iavl import ( "encoding/binary" - "fmt" "math/rand" "testing" - - "github.com/stretchr/testify/require" - dbm "github.com/tendermint/tm-db" ) func BenchmarkNodeKey(b *testing.B) { @@ -41,43 +37,3 @@ func makeHashes(b *testing.B, seed int64) [][]byte { b.StartTimer() return hashes } - -func TestNodeDBVersionMetadata(t *testing.T) { - memDB := dbm.NewMemDB() - snapDB := dbm.NewMemDB() - ndb := newNodeDB(snapDB, memDB, 0, nil) - - // Ensure metadata returns successfully when it was never saved to begin with - // i.e. for backwards compatibility. - vm, err := ndb.GetVersionMetadata(1) - require.NoError(t, err) - require.Equal(t, int64(1), vm.Version) - require.True(t, vm.Snapshot) - - x, ok := ndb.vmCache.Get(string(metadataKeyFormat.Key(1))) - require.False(t, ok) - require.Nil(t, x) - - for i := 2; i < 50000; i++ { - vm = &VersionMetadata{ - Version: int64(i), - Snapshot: i%2 == 0, - RootHash: []byte(fmt.Sprintf("%d", i)), - } - require.NoError(t, ndb.SetVersionMetadata(vm)) - - x, ok := ndb.vmCache.Get(string(metadataKeyFormat.Key(vm.Version))) - require.True(t, ok) - require.NotNil(t, x) - - vm2, err := ndb.GetVersionMetadata(vm.Version) - require.NoError(t, err) - require.Equal(t, vm, vm2) - - require.NoError(t, ndb.DeleteVersionMetadata(vm.Version)) - - x, ok = ndb.vmCache.Get(string(metadataKeyFormat.Key(vm.Version))) - require.False(t, ok) - require.Nil(t, x) - } -} diff --git a/options.go b/options.go index f0f38ab36..937e8c94d 100644 --- a/options.go +++ b/options.go @@ -1,37 +1,13 @@ package iavl -// Options define customized pruning/writing strategies for the IAVL tree +// Options define tree options. type Options struct { - KeepEvery int64 - KeepRecent int64 - Sync bool + Sync bool } -// DefaultOptions returns the default options for IAVL +// DefaultOptions returns the default options for IAVL. func DefaultOptions() *Options { return &Options{ - KeepEvery: 1, - KeepRecent: 0, - Sync: false, - } -} - -// PruningOptions returns Options with a given pruning strategy. -// Sync is set to false -func PruningOptions(keepEvery, keepRecent int64) *Options { - return &Options{ - KeepEvery: keepEvery, - KeepRecent: keepRecent, - Sync: false, - } -} - -// BenchingOptions returns Options intended for benchmark tests -// with a given pruning strategy. Sync is set to true -func BenchingOptions(keepEvery, keepRecent int64) *Options { - return &Options{ - KeepEvery: keepEvery, - KeepRecent: keepRecent, - Sync: true, + Sync: false, } } diff --git a/proto/iavl/types.proto b/proto/iavl/types.proto index 6032644fb..7a020fe7e 100644 --- a/proto/iavl/types.proto +++ b/proto/iavl/types.proto @@ -3,24 +3,6 @@ package iavl; option go_package = "iavl"; -// VersionMetadata defines the metadata associated with a committed IAVL version. -message VersionMetadata { - // tree version for the corresponding metadata - int64 version = 1; - - // the UNIX timestamp of when the metadata was committed to disk - int64 committed = 2; - - // the UNIX timestamp of when the metadata was updated - int64 updated = 3; - - // the root hash of the tree for the corresponding metadata - bytes root_hash = 4; - - // if this version corresponds to a version that is flushed to disk - bool snapshot = 5; -} - // ProofOp defines an operation used for calculating Merkle root // The data could be arbitrary format, providing nessecary data // for example neighbouring node hash diff --git a/pruning_test.go b/pruning_test.go deleted file mode 100644 index d4da3054d..000000000 --- a/pruning_test.go +++ /dev/null @@ -1,532 +0,0 @@ -package iavl - -import ( - "encoding/binary" - "fmt" - "math/rand" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - db "github.com/tendermint/tm-db" -) - -func getTestDBs() (db.DB, db.DB, func()) { - d, err := db.NewGoLevelDB("test", ".") - if err != nil { - panic(err) - } - return d, db.NewMemDB(), func() { - d.Close() - os.RemoveAll("./test.db") - } -} - -func TestSave(t *testing.T) { - db, mdb, close := getTestDBs() - defer close() - - keepRecent := rand.Int63n(8) + 2 //keep at least 2 versions in memDB - keepEvery := (rand.Int63n(3) + 1) * 100 - mt, err := NewMutableTreeWithOpts(db, mdb, 5, PruningOptions(keepEvery, keepRecent)) - require.NoError(t, err) - - // create 1000 versions - for i := 0; i < 1000; i++ { - // set 5 keys per version - for j := 0; j < 5; j++ { - key := make([]byte, 8) - val := make([]byte, 8) - binary.BigEndian.PutUint64(key, uint64(rand.Int63())) - binary.BigEndian.PutUint64(val, uint64(rand.Int63())) - mt.Set(key, val) - } - _, _, err = mt.SaveVersion() - require.Nil(t, err, "SaveVersion failed") - } - - versions := mt.AvailableVersions() - // check that all available versions are expected. - for _, v := range versions { - ver := int64(v) - // check that version is supposed to exist given pruning strategy - require.True(t, ver%keepEvery == 0 || mt.Version()-ver <= keepRecent, - "Version: %d should not exist. KeepEvery: %d, KeepRecent: %d", v, PruningOptions(keepEvery, keepRecent)) - - // check that root exists in nodeDB - lv, lverr := mt.LazyLoadVersion(ver) - require.Equal(t, ver, lv, "Version returned by LazyLoadVersion is wrong") - require.Nil(t, lverr, "Version should exist in nodeDB") - } - - // check all expected versions are available. - for j := keepEvery; j <= mt.Version(); j += keepEvery { - require.True(t, mt.VersionExists(j), "Expected snapshot version: %d to be available in nodeDB. KeepEvery: %d, KeepRecent: %d", j, PruningOptions(keepEvery, keepRecent)) - } - for k := mt.Version() - keepRecent + 1; k <= mt.Version(); k++ { - require.True(t, mt.VersionExists(k), "Expected recent version: %d to be available in nodeDB. KeepEvery: %d, KeepRecent: %d", k, PruningOptions(keepEvery, keepRecent)) - } - - // check that there only exists correct number of roots in nodeDB - roots, err := mt.ndb.getRoots() - require.Nil(t, err, "Error in getRoots") - numRoots := 1000/keepEvery + keepRecent - // decrement if there is overlap between snapshot and recent versions - if 1000%keepEvery == 0 { - numRoots-- - } - require.Equal(t, numRoots, int64(len(roots)), "nodeDB does not contain expected number of roots") -} - -func TestDeleteOrphans(t *testing.T) { - db, mdb, close := getTestDBs() - defer close() - - keepRecent := rand.Int63n(8) + 2 //keep at least 2 versions in memDB - keepEvery := (rand.Int63n(3) + 1) * 100 - mt, err := NewMutableTreeWithOpts(db, mdb, 5, PruningOptions(keepEvery, keepRecent)) - require.NoError(t, err) - - // create 1200 versions (multiple of any possible snapshotting version) - for i := 0; i < 1200; i++ { - // set 5 keys per version - for j := 0; j < 5; j++ { - key := make([]byte, 8) - val := make([]byte, 8) - binary.BigEndian.PutUint64(key, uint64(rand.Int63())) - binary.BigEndian.PutUint64(val, uint64(rand.Int63())) - mt.Set(key, val) - } - _, _, err := mt.SaveVersion() - require.Nil(t, err, "SaveVersion failed") - } - - snapfn := func(key, v []byte) { - var fromVersion, toVersion int64 - - // See comment on `orphanKeyFmt`. Note that here, `version` and - // `toVersion` are always equal. - orphanKeyFormat.Scan(key, &toVersion, &fromVersion) - - // toVersion must be snapshotVersion - require.True(t, toVersion%keepEvery == 0, "Orphan in snapshotDB has unexpected toVersion: %d. Should never have been persisted", toVersion) - } - - // check orphans in snapshotDB are expected - traverseOrphansFromDB(mt.ndb.snapshotDB, snapfn) - - recentFn := func(key, v []byte) { - var fromVersion, toVersion int64 - - // See comment on `orphanKeyFmt`. Note that here, `version` and - // `toVersion` are always equal. - orphanKeyFormat.Scan(key, &toVersion, &fromVersion) - - // toVersion must exist in recentDB - require.True(t, toVersion > mt.Version()-keepRecent, "Orphan in recentDB has unexpected fromVersion: %d. Should have been deleted", fromVersion) - } - - // check orphans in recentDB are expected - traverseOrphansFromDB(mt.ndb.recentDB, recentFn) - - // delete snapshot versions except latest version - for j := keepEvery; j < mt.Version(); j += keepEvery { - err := mt.DeleteVersion(j) - require.Nil(t, err, "Could not delete version %d", j) - } - - size := 0 - lastfn := func(key, v []byte) { - size++ - } - traverseOrphansFromDB(mt.ndb.snapshotDB, lastfn) - require.Equal(t, 0, size, "Orphans still exist in SnapshotDB") - - size = 0 - // delete all recent orphans escept latest version - for k := mt.Version() - keepRecent + 1; k < mt.Version(); k++ { - err := mt.DeleteVersion(k) - require.Nil(t, err, "Could not delete version %d", k) - } - traverseOrphansFromDB(mt.ndb.recentDB, lastfn) - require.Equal(t, 0, size, "Orphans still exist in recentDB") - - require.Equal(t, mt.nodeSize(), len(mt.ndb.nodesFromDB(mt.ndb.recentDB)), "More nodes in recentDB than expected. KeepEvery: %d, KeepRecent: %d.", PruningOptions(keepEvery, keepRecent)) - require.Equal(t, mt.nodeSize(), len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB)), "More nodes in snapshotDB than expected. KeepEvery: %d, KeepRecent: %d.", PruningOptions(keepEvery, keepRecent)) -} - -func TestReplaceKeys(t *testing.T) { - db, mdb, close := getTestDBs() - defer close() - - keepRecent := int64(1) //keep 1 version in memDB - keepEvery := int64(5) - mt, err := NewMutableTreeWithOpts(db, mdb, 5, PruningOptions(keepEvery, keepRecent)) - require.NoError(t, err) - - // Replace the same 10 keys with different values - for i := 0; i < 10; i++ { - for j := 0; j < 10; j++ { - mt.Set([]byte(fmt.Sprintf("%d", j)), []byte(fmt.Sprintf("Val:%d::Version:%d", j, i+1))) - } - _, _, err := mt.SaveVersion() - require.Nil(t, err, "Could not save version %d", i) - } - - // check that existing versions have expected values - for i := 0; i < 10; i++ { - _, val := mt.GetVersioned([]byte(fmt.Sprintf("%d", i)), 5) - require.Equal(t, fmt.Sprintf("Val:%d::Version:5", i), string(val), "Value from Version 5 unexpected") - _, val = mt.GetVersioned([]byte(fmt.Sprintf("%d", i)), 10) - require.Equal(t, fmt.Sprintf("Val:%d::Version:10", i), string(val), "Value from Version 10 unexpected") - } - - // check that all pruned versions have nil values - for v := 1; v < 10; v++ { - if v != 5 { - for i := 0; i < 10; i++ { - _, val := mt.GetVersioned([]byte(fmt.Sprintf("%d", i)), int64(v)) - require.Nil(t, val, "Pruned version: %d still has non-nil value: %v in db", v, val) - } - } - } -} - -func TestRemoveKeys(t *testing.T) { - db, mdb, close := getTestDBs() - defer close() - - keepRecent := int64(1) //keep 1 version in memDB - keepEvery := int64(10) - mt, err := NewMutableTreeWithOpts(db, mdb, 5, PruningOptions(keepEvery, keepRecent)) - require.NoError(t, err) - - for v := 0; v < 10; v++ { - for i := 0; i < 10; i++ { - mt.Set([]byte(fmt.Sprintf("v%d:%d", v, i)), []byte(fmt.Sprintf("Val:v:%d:%d", v, i))) - } - _, _, err = mt.SaveVersion() - require.NoError(t, err) - } - - numNodes := mt.nodeSize() - - for v := 0; v < 10; v++ { - for i := 0; i < 10; i++ { - key := fmt.Sprintf("v%d:%d", v, i) - _, removed := mt.Remove([]byte(key)) - require.True(t, removed, "Key %s could not be removed", key) - } - _, _, err = mt.SaveVersion() - require.NoError(t, err) - } - - require.Equal(t, numNodes, len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB)), "Number of Nodes in snapshotDB are unexpected") - - // Delete only non-empty tree in snapshotDB - err = mt.DeleteVersion(10) - require.NoError(t, err) - require.Equal(t, 0, len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB)), "Still have nodes in snapshotDB") -} - -func TestDBState(t *testing.T) { - db, mdb, close := getTestDBs() - defer close() - - keepRecent := int64(5) - keepEvery := int64(1) - mt, err := NewMutableTreeWithOpts(db, mdb, 5, PruningOptions(keepEvery, keepRecent)) - require.NoError(t, err) - - // create 5 versions - for i := 0; i < 5; i++ { - // set 5 keys per version - for j := 0; j < 5; j++ { - key := make([]byte, 8) - val := make([]byte, 8) - binary.BigEndian.PutUint64(key, uint64(rand.Int63())) - binary.BigEndian.PutUint64(val, uint64(rand.Int63())) - mt.Set(key, val) - } - _, _, err := mt.SaveVersion() - require.Nil(t, err, "SaveVersion failed") - } - - require.Equal(t, len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB)), len(mt.ndb.nodesFromDB(mt.ndb.recentDB))) - - for i := 1; i < 5; i++ { - err := mt.DeleteVersion(int64(i)) - require.Nil(t, err, "Could not delete version: %d", i) - } - - require.Equal(t, mt.nodeSize(), len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB))) - require.Equal(t, mt.nodeSize(), len(mt.ndb.nodesFromDB(mt.ndb.recentDB))) -} - -func TestSanity1(t *testing.T) { - db, mdb, close := getTestDBs() - defer close() - - keepRecent := int64(1) - keepEvery := int64(5) - mt, err := NewMutableTreeWithOpts(db, mdb, 5, PruningOptions(keepEvery, keepRecent)) - require.NoError(t, err) - - // create 5 versions - for i := 0; i < 5; i++ { - // set keys per version - // set 2 keys per version - for j := 0; j < 2; j++ { - key := []byte(fmt.Sprintf("%d: Key:v%d:i%d", rand.Int63(), i+1, j)) - val := []byte(fmt.Sprintf("Val:v%d:i%d", i, j)) - mt.Set(key, val) - } - - _, _, err := mt.SaveVersion() - require.Nil(t, err, "SaveVersion failed") - } - - require.Equal(t, mt.nodeSize(), len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB)), "SnapshotDB did not save correctly") - - for i := 1; i < 5; i++ { - err := mt.ndb.DeleteVersionFromRecent(int64(i), true) - require.NoError(t, err) - err = mt.ndb.Commit() - require.NoError(t, err) - } - - require.Equal(t, len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB)), len(mt.ndb.nodesFromDB(mt.ndb.recentDB)), "DB sizes should be the same") - - size := 0 - fn := func(k, v []byte) { - size++ - } - traverseOrphansFromDB(mt.ndb.recentDB, fn) - require.Equal(t, 0, size, "Not all orphans deleted") - - size = 0 - traverseOrphansFromDB(mt.ndb.snapshotDB, fn) - require.Equal(t, 0, size, "Not all orphans in snapshotDBdeleted") - - require.Equal(t, mt.nodeSize(), len(mt.ndb.nodesFromDB(mt.ndb.recentDB))) - require.Equal(t, mt.nodeSize(), len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB))) -} - -func TestSanity2(t *testing.T) { - db, mdb, close := getTestDBs() - defer close() - - keepRecent := int64(1) - keepEvery := int64(5) - mt, err := NewMutableTreeWithOpts(db, mdb, 0, PruningOptions(keepEvery, keepRecent)) - require.NoError(t, err) - - // create 5 versions - for i := 0; i < 5; i++ { - // set keys per version - for j := 0; j < 2; j++ { - mt.Set([]byte(fmt.Sprintf("%dKey%d|%d", rand.Int63(), i, j)), []byte(fmt.Sprintf("Val%d%d", i, j))) - } - _, _, err := mt.SaveVersion() - require.Nil(t, err, "SaveVersion failed") - } - - require.Equal(t, mt.nodeSize(), len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB)), "SnapshotDB did not save correctly") - - size := 0 - fn := func(k, v []byte) { - size++ - } - traverseOrphansFromDB(mt.ndb.snapshotDB, fn) - require.Equal(t, 0, size, "Not all orphans deleted") - - size = 0 - traverseOrphansFromDB(mt.ndb.recentDB, fn) - require.Equal(t, 0, size, "Not all orphans deleted from RecentDB") - - require.Equal(t, len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB)), len(mt.ndb.nodesFromDB(mt.ndb.recentDB)), "DB sizes should be the same") - - for i := 1; i < 5; i++ { - err := mt.DeleteVersion(int64(i)) - require.NotNil(t, err) - } - - require.Equal(t, mt.nodeSize()+size, len(mt.ndb.nodesFromDB(mt.ndb.recentDB))) - require.Equal(t, mt.nodeSize(), len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB))) -} - -func TestSanity3(t *testing.T) { - db, mdb, close := getTestDBs() - defer close() - - keepRecent := int64(4) - keepEvery := int64(100) - mt, err := NewMutableTreeWithOpts(db, mdb, 5, PruningOptions(keepEvery, keepRecent)) - require.NoError(t, err) - - // create 1000 versions - numSnapNodes := 0 - for i := 0; i < 200; i++ { - // set 5 keys per version - var key, val []byte - for j := 0; j < 5; j++ { - key = []byte(fmt.Sprintf("%d: Key:v%d:i%d", rand.Int63(), i+1, j)) - val = []byte(fmt.Sprintf("Val:v%d:i%d", i, j)) - } - mt.Set(key, val) - _, _, err = mt.SaveVersion() - if int64(i+1)%keepEvery == 0 { - numSnapNodes += mt.nodeSize() - } - require.Nil(t, err, "SaveVersion failed") - } - - fn := func(n *Node) bool { - if n.version <= 100 { - numSnapNodes-- - } - return false - } - mt.root.traverse(mt.ImmutableTree, true, fn) - - require.Equal(t, numSnapNodes, len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB))) - - err = mt.DeleteVersion(100) - require.NoError(t, err) - - require.Equal(t, mt.nodeSize(), len(mt.ndb.nodesFromDB(mt.ndb.snapshotDB))) -} - -/* Test Pruning Edge Cases */ -func TestNoSnapshots(t *testing.T) { - db, mdb, close := getTestDBs() - defer close() - - keepRecent := rand.Int63n(8) + 2 //keep at least 2 versions in memDB - mt, err := NewMutableTreeWithOpts(db, mdb, 5, PruningOptions(0, keepRecent)) // test no snapshots - require.NoError(t, err) - - for i := 0; i < 50; i++ { - // set 5 keys per version - for j := 0; j < 5; j++ { - key := make([]byte, 8) - val := make([]byte, 8) - binary.BigEndian.PutUint64(key, uint64(rand.Int63())) - binary.BigEndian.PutUint64(val, uint64(rand.Int63())) - mt.Set(key, val) - } - _, _, err := mt.SaveVersion() - require.Nil(t, err, "SaveVersion failed") - } - - versions := mt.AvailableVersions() - require.Equal(t, keepRecent, int64(len(versions)), "Versions in nodeDB not equal to recent versions") - for i := 0; int64(i) < keepRecent; i++ { - seen := false - for _, v := range versions { - if v == int(mt.Version())-i { - seen = true - } - } - require.True(t, seen, "Version %d is not available even though it is recent", mt.Version()-int64(i)) - } - - size := 0 - traverseFromDB(mt.ndb.snapshotDB, func(k, _ []byte) { - if metadataKeyFormat.Prefix() != string(k[0]) { - size++ - } - }) - - // check that nothing persisted to snapshotDB - require.Equal(t, 0, size, "SnapshotDB should be empty") -} - -func TestNoRecents(t *testing.T) { - db, _, close := getTestDBs() - defer close() - - mt, err := NewMutableTree(db, 5) - require.NoError(t, err) - - for i := 0; i < 50; i++ { - // set 5 keys per version - for j := 0; j < 5; j++ { - key := make([]byte, 8) - val := make([]byte, 8) - binary.BigEndian.PutUint64(key, uint64(rand.Int63())) - binary.BigEndian.PutUint64(val, uint64(rand.Int63())) - mt.Set(key, val) - } - _, _, err := mt.SaveVersion() - require.Nil(t, err, "SaveVersion failed") - } - - size := 0 - traverseFromDB(mt.ndb.recentDB, func(k, v []byte) { - size++ - }) - // check that nothing persisted to recentDB - require.Equal(t, 0, size, "recentDB should be empty") - - versions := mt.AvailableVersions() - require.Equal(t, 50, len(versions), "Versions in nodeDB not equal to snapshot versions") - for i := 1; i <= 50; i++ { - seen := false - for _, v := range versions { - if v == i { - seen = true - } - } - require.True(t, seen, "Version %d is not available even though it is snpashhot version", i) - } -} - -func TestValidationOptions(t *testing.T) { - db, mdb, close := getTestDBs() - defer close() - - _, err := NewMutableTreeWithOpts(db, mdb, 5, PruningOptions(2, 0)) - require.Error(t, err) - _, err = NewMutableTreeWithOpts(db, mdb, 5, PruningOptions(-1, 0)) - require.Error(t, err) - _, err = NewMutableTreeWithOpts(db, mdb, 5, PruningOptions(-1, -1)) - require.Error(t, err) -} - -// Tests that KeepRecent: 5 and KeepEvery: 5 keeps the correct versions at version 17. -func TestRecentTail(t *testing.T) { - tree, err := NewMutableTreeWithOpts(db.NewMemDB(), db.NewMemDB(), 5, &Options{ - KeepEvery: 5, - KeepRecent: 5, - }) - require.NoError(t, err) - - for v := uint8(1); v <= 17; v++ { - tree.Set([]byte("a"), []byte{v}) - tree.Set([]byte("b"), []byte{v}) - tree.Set([]byte("c"), []byte{v}) - _, version, err := tree.SaveVersion() - require.NoError(t, err) - assert.EqualValues(t, v, version) - } - - versions := tree.AvailableVersions() - assert.Equal(t, []int{5, 10, 13, 14, 15, 16, 17}, versions) - exists := make(map[int64]bool, len(versions)) - for _, v := range versions { - exists[int64(v)] = true - } - - for version := int64(1); version <= 17; version++ { - for _, key := range []string{"a", "b", "c"} { - _, value := tree.GetVersioned([]byte(key), version) - if exists[version] { - assert.EqualValues(t, []byte{uint8(version)}, value) - } else { - assert.Nil(t, value) - } - } - } -} diff --git a/testutils_test.go b/testutils_test.go index 637d3ea29..e506563f3 100644 --- a/testutils_test.go +++ b/testutils_test.go @@ -30,15 +30,9 @@ func b2i(bz []byte) int { return int(i) } -// Construct a MutableTree with random pruning parameters +// Construct a MutableTree func getTestTree(cacheSize int) (*MutableTree, error) { - keepRecent := mrand.Int63n(8) + 3 //keep at least 3 versions in memDB - keepEvery := (mrand.Int63n(3) + 1) * 100 // snapshot every {100,200,300} versions - - opts := PruningOptions(keepEvery, keepRecent) - - // Use MemDB for recentDB and snapshotDB - return NewMutableTreeWithOpts(db.NewMemDB(), db.NewMemDB(), cacheSize, opts) + return NewMutableTreeWithOpts(db.NewMemDB(), cacheSize, nil) } // Convenience for a new node diff --git a/tree_random_test.go b/tree_random_test.go index d0c7285fb..0236377cd 100644 --- a/tree_random_test.go +++ b/tree_random_test.go @@ -60,9 +60,7 @@ func testRandomOperations(t *testing.T, randSeed int64) { loadTree := func(levelDB db.DB) (tree *MutableTree, version int64, options *Options) { var err error options = &Options{ - KeepRecent: 0, - KeepEvery: 1, - Sync: r.Float64() < syncChance, + Sync: r.Float64() < syncChance, } // set the cache size regardless of whether caching is enabled. This ensures we always // call the RNG the same number of times, such that changing settings does not affect @@ -71,7 +69,7 @@ func testRandomOperations(t *testing.T, randSeed int64) { if !(r.Float64() < cacheChance) { cacheSize = 0 } - tree, err = NewMutableTreeWithOpts(levelDB, db.NewMemDB(), cacheSize, options) + tree, err = NewMutableTreeWithOpts(levelDB, cacheSize, options) require.NoError(t, err) version, err = tree.Load() require.NoError(t, err) diff --git a/tree_test.go b/tree_test.go index 77b8478f1..68acdb6a9 100644 --- a/tree_test.go +++ b/tree_test.go @@ -1379,7 +1379,7 @@ func BenchmarkTreeLoadAndDelete(b *testing.B) { func TestLoadVersionForOverwritingCase2(t *testing.T) { require := require.New(t) - tree, _ := NewMutableTreeWithOpts(db.NewMemDB(), db.NewMemDB(), 0, PruningOptions(1, 0)) + tree, _ := NewMutableTreeWithOpts(db.NewMemDB(), 0, nil) for i := byte(0); i < 20; i++ { tree.Set([]byte{i}, []byte{i}) @@ -1438,7 +1438,7 @@ func TestLoadVersionForOverwritingCase2(t *testing.T) { func TestLoadVersionForOverwritingCase3(t *testing.T) { require := require.New(t) - tree, _ := NewMutableTreeWithOpts(db.NewMemDB(), db.NewMemDB(), 0, PruningOptions(1, 0)) + tree, _ := NewMutableTreeWithOpts(db.NewMemDB(), 0, nil) for i := byte(0); i < 20; i++ { tree.Set([]byte{i}, []byte{i}) diff --git a/types.pb.go b/types.pb.go index 60690dec2..ced1f48f7 100644 --- a/types.pb.go +++ b/types.pb.go @@ -22,88 +22,6 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// VersionMetadata defines the metadata associated with a committed IAVL version. -type VersionMetadata struct { - // tree version for the corresponding metadata - Version int64 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - // the UNIX timestamp of when the metadata was committed to disk - Committed int64 `protobuf:"varint,2,opt,name=committed,proto3" json:"committed,omitempty"` - // the UNIX timestamp of when the metadata was updated - Updated int64 `protobuf:"varint,3,opt,name=updated,proto3" json:"updated,omitempty"` - // the root hash of the tree for the corresponding metadata - RootHash []byte `protobuf:"bytes,4,opt,name=root_hash,json=rootHash,proto3" json:"root_hash,omitempty"` - // if this version corresponds to a version that is flushed to disk - Snapshot bool `protobuf:"varint,5,opt,name=snapshot,proto3" json:"snapshot,omitempty"` -} - -func (m *VersionMetadata) Reset() { *m = VersionMetadata{} } -func (m *VersionMetadata) String() string { return proto.CompactTextString(m) } -func (*VersionMetadata) ProtoMessage() {} -func (*VersionMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_7ef37c124502d49e, []int{0} -} -func (m *VersionMetadata) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *VersionMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - if deterministic { - return xxx_messageInfo_VersionMetadata.Marshal(b, m, deterministic) - } else { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil - } -} -func (m *VersionMetadata) XXX_Merge(src proto.Message) { - xxx_messageInfo_VersionMetadata.Merge(m, src) -} -func (m *VersionMetadata) XXX_Size() int { - return m.Size() -} -func (m *VersionMetadata) XXX_DiscardUnknown() { - xxx_messageInfo_VersionMetadata.DiscardUnknown(m) -} - -var xxx_messageInfo_VersionMetadata proto.InternalMessageInfo - -func (m *VersionMetadata) GetVersion() int64 { - if m != nil { - return m.Version - } - return 0 -} - -func (m *VersionMetadata) GetCommitted() int64 { - if m != nil { - return m.Committed - } - return 0 -} - -func (m *VersionMetadata) GetUpdated() int64 { - if m != nil { - return m.Updated - } - return 0 -} - -func (m *VersionMetadata) GetRootHash() []byte { - if m != nil { - return m.RootHash - } - return nil -} - -func (m *VersionMetadata) GetSnapshot() bool { - if m != nil { - return m.Snapshot - } - return false -} - // ProofOp defines an operation used for calculating Merkle root // The data could be arbitrary format, providing nessecary data // for example neighbouring node hash @@ -117,7 +35,7 @@ func (m *ProofOp) Reset() { *m = ProofOp{} } func (m *ProofOp) String() string { return proto.CompactTextString(m) } func (*ProofOp) ProtoMessage() {} func (*ProofOp) Descriptor() ([]byte, []int) { - return fileDescriptor_7ef37c124502d49e, []int{1} + return fileDescriptor_7ef37c124502d49e, []int{0} } func (m *ProofOp) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -176,7 +94,7 @@ func (m *Proof) Reset() { *m = Proof{} } func (m *Proof) String() string { return proto.CompactTextString(m) } func (*Proof) ProtoMessage() {} func (*Proof) Descriptor() ([]byte, []int) { - return fileDescriptor_7ef37c124502d49e, []int{2} + return fileDescriptor_7ef37c124502d49e, []int{1} } func (m *Proof) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -213,7 +131,6 @@ func (m *Proof) GetOps() []*ProofOp { } func init() { - proto.RegisterType((*VersionMetadata)(nil), "iavl.VersionMetadata") proto.RegisterType((*ProofOp)(nil), "iavl.ProofOp") proto.RegisterType((*Proof)(nil), "iavl.Proof") } @@ -221,79 +138,18 @@ func init() { func init() { proto.RegisterFile("iavl/types.proto", fileDescriptor_7ef37c124502d49e) } var fileDescriptor_7ef37c124502d49e = []byte{ - // 268 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x44, 0x50, 0x3d, 0x4e, 0xc3, 0x30, - 0x14, 0x8e, 0x49, 0x4a, 0x93, 0x47, 0x10, 0x95, 0x27, 0x0b, 0x90, 0x89, 0x32, 0x65, 0x0a, 0x12, - 0xdc, 0x00, 0x16, 0x16, 0x04, 0xf2, 0xc0, 0xc0, 0x82, 0x0c, 0x31, 0x4a, 0x04, 0xed, 0xb3, 0x62, - 0x53, 0xa9, 0xb7, 0xe0, 0x00, 0x1c, 0x88, 0xb1, 0x23, 0x23, 0x4a, 0x2e, 0x82, 0x9e, 0x5b, 0x60, - 0xfb, 0x7e, 0xfc, 0x3e, 0x7d, 0xfe, 0x60, 0xd6, 0xe9, 0xe5, 0xeb, 0xa9, 0x5f, 0x59, 0xe3, 0x6a, - 0xdb, 0xa3, 0x47, 0x9e, 0x90, 0x52, 0x7e, 0x30, 0x38, 0xb8, 0x33, 0xbd, 0xeb, 0x70, 0x71, 0x6d, - 0xbc, 0x6e, 0xb4, 0xd7, 0x5c, 0xc0, 0x74, 0xb9, 0x91, 0x04, 0x2b, 0x58, 0x15, 0xab, 0x5f, 0xca, - 0x8f, 0x21, 0x7b, 0xc2, 0xf9, 0xbc, 0xf3, 0xde, 0x34, 0x62, 0x27, 0x78, 0xff, 0x02, 0xdd, 0xbd, - 0xd9, 0x46, 0x93, 0x17, 0x6f, 0xee, 0xb6, 0x94, 0x1f, 0x41, 0xd6, 0x23, 0xfa, 0x87, 0x56, 0xbb, - 0x56, 0x24, 0x05, 0xab, 0x72, 0x95, 0x92, 0x70, 0xa5, 0x5d, 0xcb, 0x0f, 0x21, 0x75, 0x0b, 0x6d, - 0x5d, 0x8b, 0x5e, 0x4c, 0x0a, 0x56, 0xa5, 0xea, 0x8f, 0x97, 0x97, 0x30, 0xbd, 0xed, 0x11, 0x9f, - 0x6f, 0x2c, 0xe7, 0x90, 0x50, 0xfd, 0x50, 0x29, 0x53, 0x01, 0xf3, 0x19, 0xc4, 0x2f, 0x66, 0x15, - 0x9a, 0xe4, 0x8a, 0x20, 0xbd, 0xa2, 0x3f, 0x84, 0x02, 0xb9, 0x0a, 0xb8, 0xac, 0x60, 0x12, 0x42, - 0xf8, 0x09, 0xc4, 0x68, 0x9d, 0x60, 0x45, 0x5c, 0xed, 0x9d, 0xed, 0xd7, 0x34, 0x40, 0xbd, 0x8d, - 0x57, 0xe4, 0x5c, 0xc8, 0xcf, 0x41, 0xb2, 0xf5, 0x20, 0xd9, 0xf7, 0x20, 0xd9, 0xfb, 0x28, 0xa3, - 0xf5, 0x28, 0xa3, 0xaf, 0x51, 0x46, 0xf7, 0x61, 0xad, 0xc7, 0xdd, 0x30, 0xdd, 0xf9, 0x4f, 0x00, - 0x00, 0x00, 0xff, 0xff, 0xcb, 0x45, 0xa8, 0x54, 0x4e, 0x01, 0x00, 0x00, -} - -func (m *VersionMetadata) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *VersionMetadata) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *VersionMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if m.Snapshot { - i-- - if m.Snapshot { - dAtA[i] = 1 - } else { - dAtA[i] = 0 - } - i-- - dAtA[i] = 0x28 - } - if len(m.RootHash) > 0 { - i -= len(m.RootHash) - copy(dAtA[i:], m.RootHash) - i = encodeVarintTypes(dAtA, i, uint64(len(m.RootHash))) - i-- - dAtA[i] = 0x22 - } - if m.Updated != 0 { - i = encodeVarintTypes(dAtA, i, uint64(m.Updated)) - i-- - dAtA[i] = 0x18 - } - if m.Committed != 0 { - i = encodeVarintTypes(dAtA, i, uint64(m.Committed)) - i-- - dAtA[i] = 0x10 - } - if m.Version != 0 { - i = encodeVarintTypes(dAtA, i, uint64(m.Version)) - i-- - dAtA[i] = 0x8 - } - return len(dAtA) - i, nil + // 165 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x4c, 0x2c, 0xcb, + 0xd1, 0x2f, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x01, 0x89, + 0x28, 0x39, 0x73, 0xb1, 0x07, 0x14, 0xe5, 0xe7, 0xa7, 0xf9, 0x17, 0x08, 0x09, 0x71, 0xb1, 0x80, + 0xe4, 0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x21, 0x01, 0x2e, 0xe6, 0xec, 0xd4, + 0x4a, 0x09, 0x26, 0x05, 0x46, 0x0d, 0x9e, 0x20, 0x10, 0x13, 0xa4, 0x2a, 0x25, 0xb1, 0x24, 0x51, + 0x82, 0x19, 0x2c, 0x04, 0x66, 0x2b, 0x69, 0x70, 0xb1, 0x82, 0x0d, 0x11, 0x92, 0xe7, 0x62, 0xce, + 0x2f, 0x28, 0x96, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x36, 0xe2, 0xd5, 0x03, 0xd9, 0xa0, 0x07, 0x35, + 0x3e, 0x08, 0x24, 0xe3, 0x24, 0x77, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, 0x1e, + 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, 0x51, + 0x60, 0xe7, 0x24, 0xb1, 0x81, 0xdd, 0x66, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0xf4, 0x60, 0x5f, + 0xd3, 0xaf, 0x00, 0x00, 0x00, } func (m *ProofOp) Marshal() (dAtA []byte, err error) { @@ -388,31 +244,6 @@ func encodeVarintTypes(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } -func (m *VersionMetadata) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - if m.Version != 0 { - n += 1 + sovTypes(uint64(m.Version)) - } - if m.Committed != 0 { - n += 1 + sovTypes(uint64(m.Committed)) - } - if m.Updated != 0 { - n += 1 + sovTypes(uint64(m.Updated)) - } - l = len(m.RootHash) - if l > 0 { - n += 1 + l + sovTypes(uint64(l)) - } - if m.Snapshot { - n += 2 - } - return n -} - func (m *ProofOp) Size() (n int) { if m == nil { return 0 @@ -455,170 +286,6 @@ func sovTypes(x uint64) (n int) { func sozTypes(x uint64) (n int) { return sovTypes(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } -func (m *VersionMetadata) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: VersionMetadata: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: VersionMetadata: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) - } - m.Version = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Version |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Committed", wireType) - } - m.Committed = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Committed |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Updated", wireType) - } - m.Updated = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Updated |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field RootHash", wireType) - } - var byteLen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - byteLen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if byteLen < 0 { - return ErrInvalidLengthTypes - } - postIndex := iNdEx + byteLen - if postIndex < 0 { - return ErrInvalidLengthTypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.RootHash = append(m.RootHash[:0], dAtA[iNdEx:postIndex]...) - if m.RootHash == nil { - m.RootHash = []byte{} - } - iNdEx = postIndex - case 5: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Snapshot", wireType) - } - var v int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - v |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - m.Snapshot = bool(v != 0) - default: - iNdEx = preIndex - skippy, err := skipTypes(dAtA[iNdEx:]) - if err != nil { - return err - } - if skippy < 0 { - return ErrInvalidLengthTypes - } - if (iNdEx + skippy) < 0 { - return ErrInvalidLengthTypes - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *ProofOp) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0