forked from lavanet/lava
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request lavanet#415 from lavanet/CNS-376-fixation-versioni…
…ng-and-migration CNS-376 fixation versioning and migration
- Loading branch information
Showing
17 changed files
with
359 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package common_test | ||
package common | ||
|
||
import ( | ||
"testing" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package common | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/lavanet/lava/common/types" | ||
) | ||
|
||
func prefixForErrors(from uint64) string { | ||
return fmt.Sprintf("FixationStore: migration from version %d", from) | ||
} | ||
|
||
var fixationMigrators = map[int]func(sdk.Context, *FixationStore) error{ | ||
1: fixationMigrate1to2, | ||
} | ||
|
||
func (fs *FixationStore) MigrateVersion(ctx sdk.Context) (err error) { | ||
from := fs.getVersion(ctx) | ||
to := types.FixationVersion() | ||
|
||
for from < to { | ||
function, ok := fixationMigrators[int(from)] | ||
if !ok { | ||
return fmt.Errorf("%s not available", prefixForErrors(from)) | ||
} | ||
|
||
err = function(ctx, fs) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
from += 1 | ||
} | ||
|
||
fs.setVersion(ctx, to) | ||
return nil | ||
} | ||
|
||
// fixationMigrate1to2: fix refcounts | ||
// - correct refcount of head (most recent) by adding one (because new | ||
// entries used to begin with refcount 0 instead of refcount 1) | ||
// - correct negative refcounts if found any (for extra care) | ||
func fixationMigrate1to2(ctx sdk.Context, fs *FixationStore) error { | ||
indices := fs.GetAllEntryIndices(ctx) | ||
for _, index := range indices { | ||
safeIndex, err := types.SanitizeIndex(index) | ||
if err != nil { | ||
return fmt.Errorf("%s: failed to sanitize index: %s", prefixForErrors(1), index) | ||
} | ||
blocks := fs.GetAllEntryVersions(ctx, index, true) | ||
if len(blocks) < 1 { | ||
return fmt.Errorf("%s: no versions for index: %s", prefixForErrors(1), index) | ||
} | ||
recent := blocks[len(blocks)-1] | ||
for _, block := range blocks { | ||
entry := fs.getEntry(ctx, safeIndex, block) | ||
// check for refcount overflow due to excessive putEntry | ||
if entry.Refcount > math.MaxInt64 { | ||
return fmt.Errorf("%s: entry has negative refcount index: %s", prefixForErrors(1), index) | ||
} | ||
// bump refcount of head entries (most recent version of an entry) | ||
if block == recent { | ||
entry.Refcount += 1 | ||
} | ||
// if refcount still zero, make sure StaleAt is set | ||
if entry.Refcount == 0 && entry.StaleAt == math.MaxUint64 { | ||
entry.StaleAt = uint64(ctx.BlockHeight()) + uint64(types.STALE_ENTRY_TIME) | ||
} | ||
fs.setEntry(ctx, entry) | ||
// if StaleAt is set, then start corresponding timer | ||
if entry.StaleAt != math.MaxUint { | ||
fs.tstore.AddTimerByBlockHeight(ctx, entry.StaleAt, entry.Index) | ||
} | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
package common | ||
|
||
import ( | ||
"fmt" | ||
"math" | ||
"testing" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/lavanet/lava/common/types" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
const ( | ||
mockPrefix = "mock-fs" | ||
) | ||
|
||
type mockEntry1to2 struct { | ||
index string | ||
block uint64 | ||
head bool | ||
count int | ||
before uint64 | ||
after uint64 | ||
} | ||
|
||
func TestMigrate1to2(t *testing.T) { | ||
var err error | ||
|
||
ctx, cdc := initCtx(t) | ||
fs := NewFixationStore(mockStoreKey, cdc, mockPrefix) | ||
coin := sdk.Coin{Denom: "utest", Amount: sdk.NewInt(1)} | ||
|
||
templates := []mockEntry1to2{ | ||
// entry_1 has 3 valid versions | ||
{"entry_1", 100, false, 1, 1, 1}, | ||
{"entry_1", 200, false, 2, 2, 2}, | ||
{"entry_1", 300, true, 0, 0, 1}, | ||
// entry_2 has 2 valid versions, one stale-at | ||
{"entry_2", 100, false, 1, 1, 1}, | ||
{"entry_2", 200, false, 0, 0, 0}, | ||
{"entry_2", 300, true, 0, 0, 1}, | ||
// entry_3 has 2 valid versions, head with extra refcount | ||
{"entry_3", 100, false, 0, 0, 0}, | ||
{"entry_3", 200, false, 1, 1, 1}, | ||
{"entry_3", 300, true, 1, 1, 2}, | ||
} | ||
|
||
// create entries | ||
for _, tt := range templates { | ||
err = fs.AppendEntry(ctx, tt.index, tt.block, &coin) | ||
require.Nil(t, err) | ||
for i := 0; i < tt.count; i++ { | ||
ctx = ctx.WithBlockHeight(int64(tt.block)) | ||
found := fs.GetEntry(ctx, tt.index, &coin) | ||
require.True(t, found) | ||
} | ||
} | ||
|
||
// verify entries before migration | ||
for _, tt := range templates { | ||
what := fmt.Sprintf("before: index: %s, block: %d, count: %d", tt.index, tt.block, tt.count) | ||
safeIndex, err := types.SanitizeIndex(tt.index) | ||
require.Nil(t, err, what) | ||
entry := fs.getEntry(ctx, safeIndex, tt.block) | ||
if tt.head { | ||
// adjust head entry's refcount to version 1 style | ||
entry.Refcount -= 1 | ||
fs.setEntry(ctx, entry) | ||
} | ||
require.Equal(t, tt.before, entry.Refcount, what) | ||
} | ||
|
||
// perform migration version 1 to version 2 | ||
err = fs.MigrateVersion(ctx) | ||
require.Nil(t, err, "migration version 1 to version 2") | ||
|
||
// verify entries after migration | ||
for _, tt := range templates { | ||
what := fmt.Sprintf("after: index: %s, block: %d, count: %d", tt.index, tt.block, tt.count) | ||
safeIndex, err := types.SanitizeIndex(tt.index) | ||
require.Nil(t, err, what) | ||
entry := fs.getEntry(ctx, safeIndex, tt.block) | ||
require.Equal(t, tt.after, entry.Refcount, what) | ||
if entry.Refcount == 0 { | ||
require.NotEqual(t, uint64(math.MaxUint64), entry.StaleAt, what) | ||
} else { | ||
require.Equal(t, uint64(math.MaxUint64), entry.StaleAt, what) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package common | ||
|
||
import ( | ||
"fmt" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
func (tstore *TimerStore) prefixForErrors(from uint64) string { | ||
return fmt.Sprintf("TimerStore: migration from version %d", from) | ||
} | ||
|
||
var timerMigrators = map[int]func(sdk.Context, *TimerStore) error{ | ||
// fill with map entrys like "1: timerMigrate1to2" | ||
} | ||
|
||
func (tstore *TimerStore) MigrateVersion(ctx sdk.Context) (err error) { | ||
from := tstore.getVersion(ctx) | ||
to := TimerVersion() | ||
|
||
for from < to { | ||
function, ok := timerMigrators[int(from)] | ||
if !ok { | ||
return fmt.Errorf("%s not available", prefixForErrors(from)) | ||
} | ||
|
||
err = function(ctx, tstore) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
from += 1 | ||
} | ||
|
||
tstore.setVersion(ctx, to) | ||
return nil | ||
} |
Oops, something went wrong.