Skip to content

Commit

Permalink
add InitialVersion option (#299)
Browse files Browse the repository at this point in the history
  • Loading branch information
erikgrinaker committed Sep 25, 2020
1 parent a1cd0be commit fe4c390
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 5 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Improvements

- Added `Options.InitialVersion` to specify the initial version to start new IAVL trees from.

## 0.14.0 (July 2, 2020)

**Important information:** the pruning functionality introduced with IAVL 0.13.0 via the options
Expand Down
12 changes: 12 additions & 0 deletions mutable_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ func (tree *MutableTree) LoadVersion(targetVersion int64) (int64, error) {
return 0, nil
}

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

var latestRoot []byte
Expand All @@ -337,13 +338,21 @@ func (tree *MutableTree) LoadVersion(targetVersion int64) (int64, error) {
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)
}

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,
Expand Down Expand Up @@ -439,6 +448,9 @@ func (tree *MutableTree) GetVersioned(key []byte, version int64) (
// the tree. Returns the hash and new version number.
func (tree *MutableTree) SaveVersion() ([]byte, int64, error) {
version := tree.version + 1
if version == 1 && tree.ndb.opts.InitialVersion > 0 {
version = int64(tree.ndb.opts.InitialVersion)
}

if tree.versions[version] {
// If the version already exists, return an error as we're attempting to overwrite.
Expand Down
42 changes: 42 additions & 0 deletions mutable_tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"runtime"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

db "github.com/tendermint/tm-db"
Expand Down Expand Up @@ -105,6 +106,47 @@ func TestMutableTree_DeleteVersions(t *testing.T) {
}
}

func TestMutableTree_InitialVersion(t *testing.T) {
memDB := db.NewMemDB()
tree, err := NewMutableTreeWithOpts(memDB, 0, &Options{InitialVersion: 9})
require.NoError(t, err)

tree.Set([]byte("a"), []byte{0x01})
_, version, err := tree.SaveVersion()
require.NoError(t, err)
assert.EqualValues(t, 9, version)

tree.Set([]byte("b"), []byte{0x02})
_, version, err = tree.SaveVersion()
require.NoError(t, err)
assert.EqualValues(t, 10, version)

// Reloading the tree with the same initial version is fine
tree, err = NewMutableTreeWithOpts(memDB, 0, &Options{InitialVersion: 9})
require.NoError(t, err)
version, err = tree.Load()
require.NoError(t, err)
assert.EqualValues(t, 10, version)

// Reloading the tree with an initial version beyond the lowest should error
tree, err = NewMutableTreeWithOpts(memDB, 0, &Options{InitialVersion: 10})
require.NoError(t, err)
_, err = tree.Load()
require.Error(t, err)

// Reloading the tree with a lower initial version is fine, and new versions can be produced
tree, err = NewMutableTreeWithOpts(memDB, 0, &Options{InitialVersion: 3})
require.NoError(t, err)
version, err = tree.Load()
require.NoError(t, err)
assert.EqualValues(t, 10, version)

tree.Set([]byte("c"), []byte{0x03})
_, version, err = tree.SaveVersion()
require.NoError(t, err)
assert.EqualValues(t, 11, version)
}

func BenchmarkMutableTree_Set(b *testing.B) {
db := db.NewDB("test", db.MemDBBackend, "")
t, err := NewMutableTree(db, 100000)
Expand Down
6 changes: 4 additions & 2 deletions nodedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,10 @@ func (ndb *nodeDB) saveRoot(hash []byte, version int64) error {
ndb.mtx.Lock()
defer ndb.mtx.Unlock()

if version != ndb.getLatestVersion()+1 {
return fmt.Errorf("must save consecutive versions; expected %d, got %d", ndb.getLatestVersion()+1, version)
// We allow the initial version to be arbitrary
latest := ndb.getLatestVersion()
if latest > 0 && version != latest+1 {
return fmt.Errorf("must save consecutive versions; expected %d, got %d", latest+1, version)
}

ndb.batch.Set(ndb.rootKey(version), hash)
Expand Down
11 changes: 8 additions & 3 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ package iavl

// Options define tree options.
type Options struct {
// Sync synchronously flushes all writes to storage, using e.g. the fsync syscall.
// Disabling this significantly improves performance, but can lose data on e.g. power loss.
Sync 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.
InitialVersion uint64
}

// DefaultOptions returns the default options for IAVL.
func DefaultOptions() *Options {
return &Options{
Sync: false,
}
return &Options{}
}

0 comments on commit fe4c390

Please sign in to comment.