Skip to content

Commit

Permalink
support lazy mode in LoadVersion
Browse files Browse the repository at this point in the history
Improve startup time of archive nodes.

Closes: #637
  • Loading branch information
yihuang committed Dec 1, 2022
1 parent 12381ab commit 8c7a745
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 59 deletions.
75 changes: 20 additions & 55 deletions mutable_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,78 +539,43 @@ func (tree *MutableTree) LazyLoadVersion(targetVersion int64) (int64, error) {

// Returns the version number of the latest version found
func (tree *MutableTree) LoadVersion(targetVersion int64) (int64, error) {
roots, err := tree.ndb.getRoots()
firstVersion, err := tree.ndb.firstVersion()
if err != nil {
return 0, err
}

if len(roots) == 0 {
if targetVersion <= 0 {
if !tree.skipFastStorageUpgrade {
tree.mtx.Lock()
defer tree.mtx.Unlock()
_, err := tree.enableFastStorageAndCommitIfNotEnabled()
return 0, err
}
return 0, nil
}
return 0, fmt.Errorf("no versions found while trying to load %v", targetVersion)
}

firstVersion := int64(0)
latestVersion := int64(0)

tree.mtx.Lock()
defer tree.mtx.Unlock()

var latestRoot []byte
for version, r := range roots {
tree.versions[version] = true
if version > latestVersion && (targetVersion == 0 || version <= targetVersion) {
latestVersion = version
latestRoot = r
}
if firstVersion == 0 || version < firstVersion {
firstVersion = version
}
}

if !(targetVersion == 0 || latestVersion == targetVersion) {
return latestVersion, fmt.Errorf("wanted to load target %v but only found up to %v",
targetVersion, latestVersion)
latestVersion, err := tree.ndb.getLatestVersion()
if err != nil {
return 0, err
}

if firstVersion > 0 && firstVersion < int64(tree.ndb.opts.InitialVersion) {
return latestVersion, fmt.Errorf("initial version set to %v, but found earlier version %v",
tree.ndb.opts.InitialVersion, firstVersion)
}

t := &ImmutableTree{
ndb: tree.ndb,
version: latestVersion,
skipFastStorageUpgrade: tree.skipFastStorageUpgrade,
}

if len(latestRoot) != 0 {
t.root, err = tree.ndb.GetNode(latestRoot)
if err != nil {
return 0, err
// seek to next version if targetVersion don't exists.
if targetVersion > 0 {
if targetVersion, err = tree.ndb.seekVersion(targetVersion); err != nil {
return latestVersion, err
}
}

tree.orphans = map[string]int64{}
tree.ImmutableTree = t
tree.lastSaved = t.clone()
tree.allRootLoaded = true
if _, err := tree.LazyLoadVersion(targetVersion); err != nil {
return latestVersion, err
}

if !tree.skipFastStorageUpgrade {
// Attempt to upgrade
if _, err := tree.enableFastStorageAndCommitIfNotEnabled(); err != nil {
return 0, err
if tree.ndb.opts.LoadAllRoots {
roots, err := tree.ndb.getRoots()
if err != nil {
return latestVersion, err
}
for version := range roots {
tree.versions[version] = true
}
tree.allRootLoaded = true
}

return latestVersion, nil
return tree.version, nil
}

// LoadVersionForOverwriting attempts to load a tree at a previously committed
Expand Down
4 changes: 2 additions & 2 deletions mutable_tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func TestMutableTree_DeleteVersionsRange(t *testing.T) {
require := require.New(t)

mdb := db.NewMemDB()
tree, err := NewMutableTree(mdb, 0, false)
tree, err := NewMutableTreeWithOpts(mdb, 0, &Options{LoadAllRoots: true}, false)
require.NoError(err)
const maxLength = 100
const fromLength = 10
Expand All @@ -292,7 +292,7 @@ func TestMutableTree_DeleteVersionsRange(t *testing.T) {
require.NoError(err, "SaveVersion should not fail")
}

tree, err = NewMutableTree(mdb, 0, false)
tree, err = NewMutableTreeWithOpts(mdb, 0, &Options{LoadAllRoots: true}, false)
require.NoError(err)
targetVersion, err := tree.LoadVersion(int64(maxLength))
require.NoError(err)
Expand Down
32 changes: 32 additions & 0 deletions nodedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package iavl
import (
"bytes"
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"math"
Expand Down Expand Up @@ -767,6 +768,37 @@ func (ndb *nodeDB) getPreviousVersion(version int64) (int64, error) {
return 0, nil
}

func (ndb *nodeDB) firstVersion() (int64, error) {
itr, err := dbm.IteratePrefix(ndb.db, rootKeyFormat.Key())
if err != nil {
return 0, err
}
defer itr.Close()
pversion := int64(-1)
for ; itr.Valid(); itr.Next() {
rootKeyFormat.Scan(itr.Key(), &pversion)
return pversion, nil
}
return 0, nil
}

// seekVersion returns the first version that's equal to or larger than target version.
func (ndb *nodeDB) seekVersion(version int64) (int64, error) {
db := dbm.NewPrefixDB(ndb.db, rootKeyFormat.Key())
bs := make([]byte, 8)
binary.BigEndian.PutUint64(bs, uint64(version))
itr, err := db.Iterator(bs, nil)
if err != nil {
return 0, err
}
defer itr.Close()
for ; itr.Valid(); itr.Next() {
v := int64(binary.BigEndian.Uint64(itr.Key()))
return v, nil
}
return 0, ErrVersionDoesNotExist
}

// deleteRoot deletes the root entry from disk, but not the node it points to.
func (ndb *nodeDB) deleteRoot(version int64, checkLatestVersion bool) error {
latestVersion, err := ndb.getLatestVersion()
Expand Down
4 changes: 4 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ type Options struct {
// Disabling this significantly improves performance, but can lose data on e.g. power loss.
Sync bool

// LoadAllRoots specifies if the default `LoadVersion` should load all root nodes,
// otherwise, it's similar to `LazyLoadVersion`.
LoadAllRoots bool

// InitialVersion specifies the initial version number. If any versions already exist below
// this, an error is returned when loading the tree. Only used for the initial SaveVersion()
// call.
Expand Down
3 changes: 2 additions & 1 deletion tree_random_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ func testRandomOperations(t *testing.T, randSeed int64) {
loadTree := func(levelDB db.DB) (tree *MutableTree, version int64, options *Options) {
var err error
options = &Options{
Sync: r.Float64() < syncChance,
Sync: r.Float64() < syncChance,
LoadAllRoots: true,
}
// 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
Expand Down
2 changes: 1 addition & 1 deletion tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ func TestVersionedTree(t *testing.T) {

// Recreate a new tree and load it, to make sure it works in this
// scenario.
tree, err = NewMutableTree(d, 100, false)
tree, err = NewMutableTreeWithOpts(d, 100, &Options{LoadAllRoots: true}, false)
require.NoError(err)
_, err = tree.Load()
require.NoError(err)
Expand Down

0 comments on commit 8c7a745

Please sign in to comment.