diff --git a/CHANGELOG.md b/CHANGELOG.md index dad0076bf..0ced4f1af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [#586](https://github.com/cosmos/iavl/pull/586) Remove the `RangeProof` and refactor the ics23_proof to use the internal methods. - [#640](https://github.com/cosmos/iavl/pull/640) commit `NodeDB` batch in `LoadVersionForOverwriting`. +- [#638](https://github.com/cosmos/iavl/pull/638) Make LazyLoadVersion check the opts.InitialVersion, add API `LazyLoadVersionForOverwriting`. ## 0.19.4 (October 28, 2022) diff --git a/mutable_tree.go b/mutable_tree.go index 4924264cd..e5e812979 100644 --- a/mutable_tree.go +++ b/mutable_tree.go @@ -462,17 +462,26 @@ func (tree *MutableTree) Load() (int64, error) { } // LazyLoadVersion attempts to lazy load only the specified target version -// without loading previous roots/versions. Lazy loading should be used in cases -// where only reads are expected. Any writes to a lazy loaded tree may result in -// unexpected behavior. If the targetVersion is non-positive, the latest version +// without loading previous roots/versions. If the targetVersion is non-positive, the latest version // will be loaded by default. If the latest version is non-positive, this method // performs a no-op. Otherwise, if the root does not exist, an error will be // returned. func (tree *MutableTree) LazyLoadVersion(targetVersion int64) (int64, error) { + firstVersion, err := tree.ndb.firstVersion() + if err != nil { + return 0, err + } + 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) + } + if latestVersion < targetVersion { return latestVersion, fmt.Errorf("wanted to load target %d but only found up to %d", targetVersion, latestVersion) } @@ -613,10 +622,18 @@ func (tree *MutableTree) LoadVersion(targetVersion int64) (int64, error) { return latestVersion, nil } -// LoadVersionForOverwriting attempts to load a tree at a previously committed +// loadVersionForOverwriting attempts to load a tree at a previously committed // version, or the latest version below it. Any versions greater than targetVersion will be deleted. -func (tree *MutableTree) LoadVersionForOverwriting(targetVersion int64) (int64, error) { - latestVersion, err := tree.LoadVersion(targetVersion) +func (tree *MutableTree) loadVersionForOverwriting(targetVersion int64, lazy bool) (int64, error) { + var ( + latestVersion int64 + err error + ) + if lazy { + latestVersion, err = tree.LazyLoadVersion(targetVersion) + } else { + latestVersion, err = tree.LoadVersion(targetVersion) + } if err != nil { return latestVersion, err } @@ -652,6 +669,17 @@ func (tree *MutableTree) LoadVersionForOverwriting(targetVersion int64) (int64, return latestVersion, nil } +// LoadVersionForOverwriting attempts to load a tree at a previously committed +// version, or the latest version below it. Any versions greater than targetVersion will be deleted. +func (tree *MutableTree) LoadVersionForOverwriting(targetVersion int64) (int64, error) { + return tree.loadVersionForOverwriting(targetVersion, false) +} + +// LazyLoadVersionForOverwriting is the lazy version of `LoadVersionForOverwriting`. +func (tree *MutableTree) LazyLoadVersionForOverwriting(targetVersion int64) (int64, error) { + return tree.loadVersionForOverwriting(targetVersion, true) +} + // Returns true if the tree may be auto-upgraded, false otherwise // An example of when an upgrade may be performed is when we are enaling fast storage for the first time or // need to overwrite fast nodes due to mismatch with live state. diff --git a/nodedb.go b/nodedb.go index 2d00d01ac..89a8465f3 100644 --- a/nodedb.go +++ b/nodedb.go @@ -767,6 +767,21 @@ func (ndb *nodeDB) getPreviousVersion(version int64) (int64, error) { return 0, nil } +// firstVersion returns the first version in the iavl tree, returns 0 if it's empty. +func (ndb *nodeDB) firstVersion() (int64, error) { + itr, err := dbm.IteratePrefix(ndb.db, rootKeyFormat.Key()) + if err != nil { + return 0, err + } + defer itr.Close() + if itr.Valid() { + var version int64 + rootKeyFormat.Scan(itr.Key(), &version) + return version, nil + } + return 0, nil +} + // 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()