Skip to content

Commit

Permalink
fix: make GetVersioned available after doing LazyLoadVersion (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
Woosang Son committed Apr 26, 2021
1 parent d8d511c commit 7501c2c
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 7 deletions.
24 changes: 19 additions & 5 deletions mutable_tree.go
Expand Up @@ -27,6 +27,7 @@ type MutableTree struct {
lastSaved *ImmutableTree // The most recently saved tree.
orphans map[string]int64 // Nodes removed by changes to working tree.
versions map[int64]bool // The previous, saved versions of the tree.
allRootLoaded bool // Whether all roots are loaded or not(by LazyLoadVersion)
ndb *nodeDB
}

Expand All @@ -45,6 +46,7 @@ func NewMutableTreeWithOpts(db dbm.DB, cacheSize int, opts *Options) (*MutableTr
lastSaved: head.clone(),
orphans: map[string]int64{},
versions: map[int64]bool{},
allRootLoaded: false,
ndb: ndb,
}, nil
}
Expand All @@ -57,7 +59,17 @@ func (tree *MutableTree) IsEmpty() bool {

// VersionExists returns whether or not a version exists.
func (tree *MutableTree) VersionExists(version int64) bool {
return tree.versions[version]
if tree.allRootLoaded {
return tree.versions[version]
}

has, ok := tree.versions[version]
if ok {
return has
}
has, _ = tree.ndb.HasRoot(version)
tree.versions[version] = has
return has
}

// AvailableVersions returns all available versions in ascending order
Expand Down Expand Up @@ -372,6 +384,7 @@ func (tree *MutableTree) LoadVersion(targetVersion int64) (int64, error) {
tree.orphans = map[string]int64{}
tree.ImmutableTree = t
tree.lastSaved = t.clone()
tree.allRootLoaded = true

return latestVersion, nil
}
Expand Down Expand Up @@ -413,11 +426,13 @@ func (tree *MutableTree) GetImmutable(version int64) (*ImmutableTree, error) {
if rootHash == nil {
return nil, ErrVersionDoesNotExist
} else if len(rootHash) == 0 {
tree.versions[version] = true
return &ImmutableTree{
ndb: tree.ndb,
version: version,
}, nil
}
tree.versions[version] = true
return &ImmutableTree{
root: tree.ndb.GetNode(rootHash),
ndb: tree.ndb,
Expand All @@ -441,7 +456,7 @@ func (tree *MutableTree) Rollback() {
func (tree *MutableTree) GetVersioned(key []byte, version int64) (
index int64, value []byte,
) {
if tree.versions[version] {
if tree.VersionExists(version) {
t, err := tree.GetImmutable(version)
if err != nil {
return -1, nil
Expand All @@ -459,7 +474,7 @@ func (tree *MutableTree) SaveVersion() ([]byte, int64, error) {
version = int64(tree.ndb.opts.InitialVersion)
}

if tree.versions[version] {
if tree.VersionExists(version) {
// If the version already exists, return an error as we're attempting to overwrite.
// However, the same hash means idempotent (i.e. no-op).
existingHash, err := tree.ndb.getRoot(version)
Expand Down Expand Up @@ -525,10 +540,9 @@ func (tree *MutableTree) deleteVersion(version int64) error {
if version == tree.version {
return errors.Errorf("cannot delete latest saved version (%d)", version)
}
if _, ok := tree.versions[version]; !ok {
if !tree.VersionExists(version) {
return errors.Wrap(ErrVersionDoesNotExist, "")
}

if err := tree.ndb.DeleteVersion(version, true); err != nil {
return err
}
Expand Down
70 changes: 70 additions & 0 deletions mutable_tree_test.go
Expand Up @@ -288,3 +288,73 @@ func BenchmarkMutableTree_Set(b *testing.B) {
t.Set(randBytes(10), []byte{})
}
}

func prepareTree(t *testing.T) *MutableTree {
mdb := db.NewMemDB()
tree, err := NewMutableTree(mdb, 1000)
require.NoError(t, err)
for i := 0; i < 100; i++ {
tree.Set([]byte{byte(i)}, []byte("a"))
}
_, ver, err := tree.SaveVersion()
require.True(t, ver == 1)
require.NoError(t, err)
for i := 0; i < 100; i++ {
tree.Set([]byte{byte(i)}, []byte("b"))
}
_, ver, err = tree.SaveVersion()
require.True(t, ver == 2)
require.NoError(t, err)
newTree, err := NewMutableTree(mdb, 1000)
require.NoError(t, err)

return newTree
}

func TestMutableTree_VersionExists(t *testing.T) {
tree := prepareTree(t)
require.True(t, tree.VersionExists(1))
require.True(t, tree.VersionExists(2))
require.False(t, tree.VersionExists(3))
}

func checkGetVersioned(t *testing.T, tree *MutableTree, version, index int64, key, value []byte) {
idx, val := tree.GetVersioned(key, version)
require.True(t, idx == index)
require.True(t, bytes.Equal(val, value))
}

func TestMutableTree_GetVersioned(t *testing.T) {
tree := prepareTree(t)
ver, err := tree.LazyLoadVersion(1)
require.True(t, ver == 1)
require.NoError(t, err)
// check key of unloaded version
checkGetVersioned(t, tree, 1, 1, []byte{1}, []byte("a"))
checkGetVersioned(t, tree, 2, 1, []byte{1}, []byte("b"))
checkGetVersioned(t, tree, 3, -1, []byte{1}, nil)

tree = prepareTree(t)
ver, err = tree.LazyLoadVersion(2)
require.True(t, ver == 2)
require.NoError(t, err)
checkGetVersioned(t, tree, 1, 1, []byte{1}, []byte("a"))
checkGetVersioned(t, tree, 2, 1, []byte{1}, []byte("b"))
checkGetVersioned(t, tree, 3, -1, []byte{1}, nil)
}

func TestMutableTree_DeleteVersion(t *testing.T) {
tree := prepareTree(t)
ver, err := tree.LazyLoadVersion(2)
require.True(t, ver == 2)
require.NoError(t, err)

require.NoError(t, tree.DeleteVersion(1))

require.False(t, tree.VersionExists(1))
require.True(t, tree.VersionExists(2))
require.False(t, tree.VersionExists(3))

// cannot delete latest version
require.Error(t, tree.DeleteVersion(2))
}
4 changes: 4 additions & 0 deletions nodedb.go
Expand Up @@ -543,6 +543,10 @@ func (ndb *nodeDB) Commit() error {
return nil
}

func (ndb *nodeDB) HasRoot(version int64) (bool, error) {
return ndb.db.Has(ndb.rootKey(version))
}

func (ndb *nodeDB) getRoot(version int64) ([]byte, error) {
return ndb.db.Get(ndb.rootKey(version))
}
Expand Down
4 changes: 2 additions & 2 deletions proof_range.go
Expand Up @@ -541,7 +541,7 @@ func (t *ImmutableTree) GetRangeWithProof(startKey []byte, endKey []byte, limit
// GetVersionedWithProof gets the value under the key at the specified version
// if it exists, or returns nil.
func (tree *MutableTree) GetVersionedWithProof(key []byte, version int64) ([]byte, *RangeProof, error) {
if tree.versions[version] {
if tree.VersionExists(version) {
t, err := tree.GetImmutable(version)
if err != nil {
return nil, nil, err
Expand All @@ -557,7 +557,7 @@ func (tree *MutableTree) GetVersionedWithProof(key []byte, version int64) ([]byt
func (tree *MutableTree) GetVersionedRangeWithProof(startKey, endKey []byte, limit int, version int64) (
keys, values [][]byte, proof *RangeProof, err error) {

if tree.versions[version] {
if tree.VersionExists(version) {
t, err := tree.GetImmutable(version)
if err != nil {
return nil, nil, nil, err
Expand Down

0 comments on commit 7501c2c

Please sign in to comment.