Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement LazyLoadVersion #148

Merged
merged 7 commits into from
Jul 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 50 additions & 3 deletions mutable_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,21 +224,66 @@ func (tree *MutableTree) Load() (int64, error) {
return tree.LoadVersion(int64(0))
}

// 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
// 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) {
latestVersion := tree.ndb.getLatestVersion()
if latestVersion < targetVersion {
return latestVersion, fmt.Errorf("wanted to load target %d but only found up to %d", targetVersion, latestVersion)
}

// no versions have been saved if the latest version is non-positive
if latestVersion <= 0 {
return 0, nil
}

// default to the latest version if the targeted version is non-positive
if targetVersion <= 0 {
targetVersion = latestVersion
}

rootHash := tree.ndb.getRoot(targetVersion)
if rootHash == nil {
return latestVersion, ErrVersionDoesNotExist
}

tree.versions[targetVersion] = true

iTree := &ImmutableTree{
ndb: tree.ndb,
version: targetVersion,
root: tree.ndb.GetNode(rootHash),
}

tree.orphans = map[string]int64{}
tree.ImmutableTree = iTree
tree.lastSaved = iTree.clone()

return targetVersion, nil
}

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

if len(roots) == 0 {
return 0, nil
}

latestVersion := int64(0)

var latestRoot []byte
for version, r := range roots {
tree.versions[version] = true
if version > latestVersion &&
(targetVersion == 0 || version <= targetVersion) {
if version > latestVersion && (targetVersion == 0 || version <= targetVersion) {
latestVersion = version
latestRoot = r
}
Expand All @@ -253,13 +298,15 @@ func (tree *MutableTree) LoadVersion(targetVersion int64) (int64, error) {
ndb: tree.ndb,
version: latestVersion,
}

if len(latestRoot) != 0 {
t.root = tree.ndb.GetNode(latestRoot)
}

tree.orphans = map[string]int64{}
tree.ImmutableTree = t
tree.lastSaved = t.clone()

return latestVersion, nil
}

Expand All @@ -270,7 +317,7 @@ func (tree *MutableTree) LoadVersionForOverwriting(targetVersion int64) (int64,
if err != nil {
return latestVersion, err
}
tree.deleteVersionsFrom(targetVersion+1)
tree.deleteVersionsFrom(targetVersion + 1)
return targetVersion, nil
}

Expand Down
39 changes: 39 additions & 0 deletions tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package iavl
import (
"bytes"
"flag"
"fmt"
"os"
"runtime"
"strconv"
Expand Down Expand Up @@ -1134,6 +1135,44 @@ func TestRollback(t *testing.T) {
require.Equal([]byte("v"), val)
}

func TestLazyLoadVersion(t *testing.T) {
mdb := db.NewMemDB()
tree := NewMutableTree(mdb, 0)
maxVersions := 10

version, err := tree.LazyLoadVersion(0)
require.NoError(t, err, "unexpected error")
require.Equal(t, version, int64(0), "expected latest version to be zero")

for i := 0; i < maxVersions; i++ {
tree.Set([]byte(fmt.Sprintf("key_%d", i+1)), []byte(fmt.Sprintf("value_%d", i+1)))

_, _, err := tree.SaveVersion()
require.NoError(t, err, "SaveVersion should not fail")
}

// require the ability to lazy load the latest version
version, err = tree.LazyLoadVersion(int64(maxVersions))
require.NoError(t, err, "unexpected error when lazy loading version")
require.Equal(t, version, int64(maxVersions))

_, value := tree.Get([]byte(fmt.Sprintf("key_%d", maxVersions)))
require.Equal(t, value, []byte(fmt.Sprintf("value_%d", maxVersions)), "unexpected value")

// require the ability to lazy load an older version
version, err = tree.LazyLoadVersion(int64(maxVersions - 1))
require.NoError(t, err, "unexpected error when lazy loading version")
require.Equal(t, version, int64(maxVersions-1))

_, value = tree.Get([]byte(fmt.Sprintf("key_%d", maxVersions-1)))
require.Equal(t, value, []byte(fmt.Sprintf("value_%d", maxVersions-1)), "unexpected value")

// require the inability to lazy load a non-valid version
version, err = tree.LazyLoadVersion(int64(maxVersions + 1))
require.Error(t, err, "expected error when lazy loading version")
require.Equal(t, version, int64(maxVersions))
}

func TestOverwrite(t *testing.T) {
require := require.New(t)

Expand Down