Skip to content

Commit

Permalink
expose isUpgradeable method on mutable tree and unit test (#17)
Browse files Browse the repository at this point in the history
* expose isUpgradeable method on mutable tree and unit test

* go fmt
  • Loading branch information
p0mvn committed Feb 11, 2022
1 parent dd48987 commit 0d949ef
Show file tree
Hide file tree
Showing 12 changed files with 81 additions and 68 deletions.
2 changes: 1 addition & 1 deletion export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func setupExportTreeRandom(t *testing.T) *ImmutableTree {
keySize = 16
valueSize = 16

versions = 8 // number of versions to generate
versions = 8 // number of versions to generate
versionOps = 1024 // number of operations (create/update/delete) per version
updateRatio = 0.4 // ratio of updates out of all operations
deleteRatio = 0.2 // ratio of deletes out of all operations
Expand Down
2 changes: 1 addition & 1 deletion fast_iterator.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
var errFastIteratorNilNdbGiven = errors.New("fast iterator must be created with a nodedb but it was nil")

// FastIterator is a dbm.Iterator for ImmutableTree
// it iterates over the latest state via fast nodes,
// it iterates over the latest state via fast nodes,
// taking advantage of keys being located in sequence in the underlying database.
type FastIterator struct {
start, end []byte
Expand Down
6 changes: 3 additions & 3 deletions fast_node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ func TestFastNode_encode_decode(t *testing.T) {
"nil": {nil, "", true},
"empty": {&FastNode{}, "0000", false},
"inner": {&FastNode{
key: []byte{0x4},
versionLastUpdatedAt: 1,
value: []byte{0x2},
key: []byte{0x4},
versionLastUpdatedAt: 1,
value: []byte{0x2},
}, "020102", false},
}
for name, tc := range testcases {
Expand Down
6 changes: 3 additions & 3 deletions iterator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ func TestIterator_Basic_Full_Ascending_Success(t *testing.T) {
t.Run("Unsaved Fast Iterator", func(t *testing.T) {
itr, mirror := setupUnsavedFastIterator(t, config)
require.True(t, itr.Valid())
require.Equal(t, 25 - 25 / 4 + 1, len(mirror)) // to account for removals
require.Equal(t, 25-25/4+1, len(mirror)) // to account for removals
performTest(t, itr, mirror)
})
}
Expand Down Expand Up @@ -267,7 +267,7 @@ func TestIterator_Basic_Full_Descending_Success(t *testing.T) {

t.Run("Unsaved Fast Iterator", func(t *testing.T) {
itr, mirror := setupUnsavedFastIterator(t, config)
require.Equal(t, 25 - 25 / 4 + 1, len(mirror)) // to account for removals
require.Equal(t, 25-25/4+1, len(mirror)) // to account for removals
require.True(t, itr.Valid())
performTest(t, itr, mirror)
})
Expand Down Expand Up @@ -393,7 +393,7 @@ func setupUnsavedFastIterator(t *testing.T, config *iteratorTestConfig) (dbm.Ite

if len(mergedMirror) > 0 {
// Remove random keys
for i := 0; i < len(mergedMirror) / 4; i++ {
for i := 0; i < len(mergedMirror)/4; i++ {
randIndex := rand.Intn(len(mergedMirror))
keyToRemove := mergedMirror[randIndex][0]

Expand Down
13 changes: 10 additions & 3 deletions mutable_tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -504,14 +504,21 @@ func (tree *MutableTree) LoadVersionForOverwriting(targetVersion int64) (int64,
return latestVersion, nil
}

// 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.
func (tree *MutableTree) IsUpgradeable() bool {
return !tree.ndb.hasUpgradedToFastStorage() || tree.ndb.shouldForceFastStorageUpgrade()
}

// enableFastStorageAndCommitIfNotEnabled if nodeDB doesn't mark fast storage as enabled, enable it, and commit the update.
// Checks whether the fast cache on disk matches latest live state. If not, deletes all existing fast nodes and repopulates them
// from latest tree.
func (tree *MutableTree) enableFastStorageAndCommitIfNotEnabled() (bool, error) {
shouldForceUpdate := tree.ndb.shouldForceFastStorageUpdate()
isFastStorageEnabled := tree.ndb.hasUpgradedToFastStorage()
shouldForceUpdate := tree.ndb.shouldForceFastStorageUpgrade()
isFastStorageEnabled := tree.ndb.hasUpgradedToFastStorage()

if isFastStorageEnabled && !shouldForceUpdate {
if !tree.IsUpgradeable() {
return false, nil
}

Expand Down
16 changes: 13 additions & 3 deletions mutable_tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -678,9 +678,11 @@ func TestUpgradeStorageToFast_LatestVersion_Success(t *testing.T) {
randomizeTreeAndMirror(t, tree, mirror)

// Enable fast storage
require.True(t, tree.IsUpgradeable())
enabled, err := tree.enableFastStorageAndCommitIfNotEnabled()
require.NoError(t, err)
require.True(t, enabled)
require.False(t, tree.IsUpgradeable())

require.True(t, tree.IsFastCacheEnabled())
}
Expand All @@ -699,10 +701,12 @@ func TestUpgradeStorageToFast_AlreadyUpgraded_Success(t *testing.T) {
randomizeTreeAndMirror(t, tree, mirror)

// Enable fast storage
require.True(t, tree.IsUpgradeable())
enabled, err := tree.enableFastStorageAndCommitIfNotEnabled()
require.NoError(t, err)
require.True(t, enabled)
require.True(t, tree.IsFastCacheEnabled())
require.False(t, tree.IsUpgradeable())

// Test enabling fast storage when already enabled
enabled, err = tree.enableFastStorageAndCommitIfNotEnabled()
Expand Down Expand Up @@ -799,7 +803,7 @@ func TestFastStorageReUpgradeProtection_NoForceUpgrade_Success(t *testing.T) {

// Ensure that the right branch of enableFastStorageAndCommitIfNotEnabled will be triggered
require.True(t, tree.IsFastCacheEnabled())
require.False(t, tree.ndb.shouldForceFastStorageUpdate())
require.False(t, tree.ndb.shouldForceFastStorageUpgrade())

enabled, err := tree.enableFastStorageAndCommitIfNotEnabled()
require.NoError(t, err)
Expand Down Expand Up @@ -846,7 +850,7 @@ func TestFastStorageReUpgradeProtection_ForceUpgradeFirstTime_NoForceSecondTime_
// upgrade and then commits them all in the end.
updatedExpectedStorageVersion := make([]byte, len(expectedStorageVersion))
copy(updatedExpectedStorageVersion, expectedStorageVersion)
updatedExpectedStorageVersion[len(updatedExpectedStorageVersion) - 1]++
updatedExpectedStorageVersion[len(updatedExpectedStorageVersion)-1]++
batchMock.EXPECT().Delete(fastKeyFormat.Key(fastNodeKeyToDelete)).Return(nil).Times(1)
batchMock.EXPECT().Set(metadataKeyFormat.Key([]byte(storageVersionKey)), updatedExpectedStorageVersion).Return(nil).Times(1)
batchMock.EXPECT().Write().Return(nil).Times(1)
Expand Down Expand Up @@ -886,7 +890,7 @@ func TestFastStorageReUpgradeProtection_ForceUpgradeFirstTime_NoForceSecondTime_

// Ensure that the right branch of enableFastStorageAndCommitIfNotEnabled will be triggered
require.True(t, tree.IsFastCacheEnabled())
require.True(t, tree.ndb.shouldForceFastStorageUpdate())
require.True(t, tree.ndb.shouldForceFastStorageUpgrade())

// Actual method under test
enabled, err := tree.enableFastStorageAndCommitIfNotEnabled()
Expand All @@ -904,16 +908,19 @@ func TestUpgradeStorageToFast_Integration_Upgraded_FastIterator_Success(t *testi
tree, mirror := setupTreeAndMirrorForUpgrade(t)

require.False(t, tree.IsFastCacheEnabled())
require.True(t, tree.IsUpgradeable())

// Should auto enable in save version
_, _, err := tree.SaveVersion()
require.NoError(t, err)

require.True(t, tree.IsFastCacheEnabled())
require.False(t, tree.IsUpgradeable())

sut, _ := NewMutableTree(tree.ndb.db, 1000)

require.False(t, sut.IsFastCacheEnabled())
require.False(t, sut.IsUpgradeable()) // upgraded in save version

// Load version - should auto enable fast storage
version, err := sut.Load()
Expand Down Expand Up @@ -954,16 +961,19 @@ func TestUpgradeStorageToFast_Integration_Upgraded_GetFast_Success(t *testing.T)
tree, mirror := setupTreeAndMirrorForUpgrade(t)

require.False(t, tree.IsFastCacheEnabled())
require.True(t, tree.IsUpgradeable())

// Should auto enable in save version
_, _, err := tree.SaveVersion()
require.NoError(t, err)

require.True(t, tree.IsFastCacheEnabled())
require.False(t, tree.IsUpgradeable())

sut, _ := NewMutableTree(tree.ndb.db, 1000)

require.False(t, sut.IsFastCacheEnabled())
require.False(t, sut.IsUpgradeable()) // upgraded in save version

// LazyLoadVersion - should auto enable fast storage
version, err := sut.LazyLoadVersion(1)
Expand Down
10 changes: 5 additions & 5 deletions nodedb.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ var (
rootKeyFormat = NewKeyFormat('r', int64Size) // r<version>
)

var(
var (
errInvalidFastStorageVersion = fmt.Sprintf("Fast storage version must be in the format <storage version>%s<latest fast cache version>", fastStorageVersionDelimiter)
)

Expand Down Expand Up @@ -253,16 +253,16 @@ func (ndb *nodeDB) getStorageVersion() string {
return ndb.storageVersion
}

// Returns true if the upgrade to fast storage has occurred, false otherwise.
// Returns true if the upgrade to latest storage version has been performed, false otherwise.
func (ndb *nodeDB) hasUpgradedToFastStorage() bool {
return ndb.getStorageVersion() >= fastStorageVersionValue
}

// Returns true if the upgrade to fast storage has occurred but it does not match the live state, false otherwise.
// When the live state is not matched, we must force reupgrade.
// We determine this by checking the version of the live state and the version of the live state wheb
// fast storage was updated on disk the last time.
func (ndb *nodeDB) shouldForceFastStorageUpdate() bool {
// We determine this by checking the version of the live state and the version of the live state when
// latest storage was updated on disk the last time.
func (ndb *nodeDB) shouldForceFastStorageUpgrade() bool {
versions := strings.Split(ndb.storageVersion, fastStorageVersionDelimiter)

if len(versions) == 2 {
Expand Down
32 changes: 16 additions & 16 deletions nodedb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func TestSetStorageVersion_Success(t *testing.T) {

err := ndb.setFastStorageVersionToBatch()
require.NoError(t, err)
require.Equal(t, expectedVersion + fastStorageVersionDelimiter + strconv.Itoa(int(ndb.getLatestVersion())), string(ndb.getStorageVersion()))
require.Equal(t, expectedVersion+fastStorageVersionDelimiter+strconv.Itoa(int(ndb.getLatestVersion())), string(ndb.getStorageVersion()))
ndb.batch.Write()
}

Expand All @@ -88,7 +88,7 @@ func TestSetStorageVersion_DBFailure_OldKept(t *testing.T) {
dbMock := mock.NewMockDB(ctrl)
batchMock := mock.NewMockBatch(ctrl)
rIterMock := mock.NewMockIterator(ctrl)

expectedErrorMsg := "some db error"

expectedFastCacheVersion := 2
Expand All @@ -102,7 +102,7 @@ func TestSetStorageVersion_DBFailure_OldKept(t *testing.T) {
rIterMock.EXPECT().Close().Return(nil).Times(1)

dbMock.EXPECT().ReverseIterator(gomock.Any(), gomock.Any()).Return(rIterMock, nil).Times(1)
batchMock.EXPECT().Set([]byte(metadataKeyFormat.Key([]byte(storageVersionKey))), []byte(fastStorageVersionValue + fastStorageVersionDelimiter + strconv.Itoa(expectedFastCacheVersion))).Return(errors.New(expectedErrorMsg)).Times(1)
batchMock.EXPECT().Set([]byte(metadataKeyFormat.Key([]byte(storageVersionKey))), []byte(fastStorageVersionValue+fastStorageVersionDelimiter+strconv.Itoa(expectedFastCacheVersion))).Return(errors.New(expectedErrorMsg)).Times(1)

ndb := newNodeDB(dbMock, 0, nil)
require.Equal(t, defaultStorageVersionValue, string(ndb.getStorageVersion()))
Expand All @@ -117,7 +117,7 @@ func TestSetStorageVersion_InvalidVersionFailure_OldKept(t *testing.T) {
ctrl := gomock.NewController(t)
dbMock := mock.NewMockDB(ctrl)
batchMock := mock.NewMockBatch(ctrl)

expectedErrorMsg := errInvalidFastStorageVersion

invalidStorageVersion := fastStorageVersionValue + fastStorageVersionDelimiter + "1" + fastStorageVersionDelimiter + "2"
Expand All @@ -142,7 +142,7 @@ func TestSetStorageVersion_FastVersionFirst_VersionAppended(t *testing.T) {

err := ndb.setFastStorageVersionToBatch()
require.NoError(t, err)
require.Equal(t, fastStorageVersionValue + fastStorageVersionDelimiter + strconv.Itoa(int(ndb.latestVersion)), ndb.storageVersion)
require.Equal(t, fastStorageVersionValue+fastStorageVersionDelimiter+strconv.Itoa(int(ndb.latestVersion)), ndb.storageVersion)
}

func TestSetStorageVersion_FastVersionSecond_VersionAppended(t *testing.T) {
Expand All @@ -151,12 +151,12 @@ func TestSetStorageVersion_FastVersionSecond_VersionAppended(t *testing.T) {
ndb.latestVersion = 100

storageVersionBytes := []byte(fastStorageVersionValue)
storageVersionBytes[len(fastStorageVersionValue) - 1]++ // increment last byte
storageVersionBytes[len(fastStorageVersionValue)-1]++ // increment last byte
ndb.storageVersion = string(storageVersionBytes)

err := ndb.setFastStorageVersionToBatch()
require.NoError(t, err)
require.Equal(t, string(storageVersionBytes) + fastStorageVersionDelimiter + strconv.Itoa(int(ndb.latestVersion)), ndb.storageVersion)
require.Equal(t, string(storageVersionBytes)+fastStorageVersionDelimiter+strconv.Itoa(int(ndb.latestVersion)), ndb.storageVersion)
}

func TestSetStorageVersion_SameVersionTwice(t *testing.T) {
Expand All @@ -165,12 +165,12 @@ func TestSetStorageVersion_SameVersionTwice(t *testing.T) {
ndb.latestVersion = 100

storageVersionBytes := []byte(fastStorageVersionValue)
storageVersionBytes[len(fastStorageVersionValue) - 1]++ // increment last byte
storageVersionBytes[len(fastStorageVersionValue)-1]++ // increment last byte
ndb.storageVersion = string(storageVersionBytes)

err := ndb.setFastStorageVersionToBatch()
require.NoError(t, err)
newStorageVersion := string(storageVersionBytes) + fastStorageVersionDelimiter + strconv.Itoa(int(ndb.latestVersion))
newStorageVersion := string(storageVersionBytes) + fastStorageVersionDelimiter + strconv.Itoa(int(ndb.latestVersion))
require.Equal(t, newStorageVersion, ndb.storageVersion)

err = ndb.setFastStorageVersionToBatch()
Expand All @@ -185,25 +185,25 @@ func TestShouldForceFastStorageUpdate_DefaultVersion_True(t *testing.T) {
ndb.storageVersion = defaultStorageVersionValue
ndb.latestVersion = 100

require.False(t, ndb.shouldForceFastStorageUpdate())
require.False(t, ndb.shouldForceFastStorageUpgrade())
}

func TestShouldForceFastStorageUpdate_FastVersion_Greater_True(t *testing.T) {
db := db.NewMemDB()
ndb := newNodeDB(db, 0, nil)
ndb.latestVersion = 100
ndb.storageVersion = fastStorageVersionValue + fastStorageVersionDelimiter + strconv.Itoa(int(ndb.latestVersion + 1))
ndb.storageVersion = fastStorageVersionValue + fastStorageVersionDelimiter + strconv.Itoa(int(ndb.latestVersion+1))

require.True(t, ndb.shouldForceFastStorageUpdate())
require.True(t, ndb.shouldForceFastStorageUpgrade())
}

func TestShouldForceFastStorageUpdate_FastVersion_Smaller_True(t *testing.T) {
db := db.NewMemDB()
ndb := newNodeDB(db, 0, nil)
ndb.latestVersion = 100
ndb.storageVersion = fastStorageVersionValue + fastStorageVersionDelimiter + strconv.Itoa(int(ndb.latestVersion - 1))
ndb.storageVersion = fastStorageVersionValue + fastStorageVersionDelimiter + strconv.Itoa(int(ndb.latestVersion-1))

require.True(t, ndb.shouldForceFastStorageUpdate())
require.True(t, ndb.shouldForceFastStorageUpgrade())
}

func TestShouldForceFastStorageUpdate_FastVersion_Match_False(t *testing.T) {
Expand All @@ -212,7 +212,7 @@ func TestShouldForceFastStorageUpdate_FastVersion_Match_False(t *testing.T) {
ndb.latestVersion = 100
ndb.storageVersion = fastStorageVersionValue + fastStorageVersionDelimiter + strconv.Itoa(int(ndb.latestVersion))

require.False(t, ndb.shouldForceFastStorageUpdate())
require.False(t, ndb.shouldForceFastStorageUpgrade())
}

func TestIsFastStorageEnabled_True(t *testing.T) {
Expand All @@ -230,7 +230,7 @@ func TestIsFastStorageEnabled_False(t *testing.T) {
ndb.latestVersion = 100
ndb.storageVersion = defaultStorageVersionValue

require.False(t, ndb.shouldForceFastStorageUpdate())
require.False(t, ndb.shouldForceFastStorageUpgrade())
}

func makeHashes(b *testing.B, seed int64) [][]byte {
Expand Down
Loading

0 comments on commit 0d949ef

Please sign in to comment.