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

Problem: memiavl snapshot rewrite could rollback state #1123

Merged
merged 6 commits into from
Aug 5, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
- [#1087](https://github.com/crypto-org-chain/cronos/pull/1087) memiavl fix LastCommitID when memiavl db not loaded.
- [#1088](https://github.com/crypto-org-chain/cronos/pull/1088) memiavl fix empty value in write-ahead-log replaying.
- [#1102](https://github.com/crypto-org-chain/cronos/pull/1102) avoid duplicate cache events emitted from ibc and gravity hook.
- [#1123](https://github.com/crypto-org-chain/cronos/pull/1123) Fix memiavl snapshot switching

### Features

Expand Down
26 changes: 26 additions & 0 deletions memiavl/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strconv"
"strings"
"sync"
"time"

storetypes "github.com/cosmos/cosmos-sdk/store/types"
"github.com/tendermint/tendermint/libs/log"
Expand Down Expand Up @@ -329,6 +330,18 @@ func (db *DB) checkAsyncCommit() error {
return nil
}

// CommittedVersion returns the latest version written in wal, or snapshot version if wal is empty.
func (db *DB) CommittedVersion() (int64, error) {
lastIndex, err := db.wal.LastIndex()
if err != nil {
return 0, err
}
if lastIndex == 0 {
return db.SnapshotVersion(), nil
}
return walVersion(lastIndex, db.initialVersion), nil
}

// checkBackgroundSnapshotRewrite check the result of background snapshot rewrite, cleans up the old snapshots and switches to a new multitree
func (db *DB) checkBackgroundSnapshotRewrite() error {
// check the completeness of background snapshot rewriting
Expand All @@ -341,6 +354,19 @@ func (db *DB) checkBackgroundSnapshotRewrite() error {
return fmt.Errorf("background snapshot rewriting failed: %w", result.err)
}

// wait for potential pending wal writings to finish, to make sure we catch up to latest state.
// in real world, block execution should be slower than wal writing, so this should not block for long.
for {
committedVersion, err := db.CommittedVersion()
if err != nil {
return fmt.Errorf("get wal version failed: %w", err)
}
if db.lastCommitInfo.Version == committedVersion {
break
}
time.Sleep(time.Nanosecond)
mmsqe marked this conversation as resolved.
Show resolved Hide resolved
}
mmsqe marked this conversation as resolved.
Show resolved Hide resolved

// catchup the remaining wal
if err := result.mtree.CatchupWAL(db.wal, 0); err != nil {
return fmt.Errorf("catchup failed: %w", err)
Expand Down
23 changes: 23 additions & 0 deletions memiavl/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -393,3 +393,26 @@ func TestExclusiveLock(t *testing.T) {
_, err = Load(dir, Options{})
require.NoError(t, err)
}

func TestFastCommit(t *testing.T) {
dir := t.TempDir()

db, err := Load(dir, Options{CreateIfMissing: true, InitialStores: []string{"test"}, SnapshotInterval: 3, AsyncCommitBuffer: 10})
require.NoError(t, err)

cs := iavl.ChangeSet{
Pairs: []*iavl.KVPair{
{Key: []byte("hello1"), Value: make([]byte, 1024*1024)},
},
}

// the bug reproduce when the wal segment is full and create a new one,
yihuang marked this conversation as resolved.
Show resolved Hide resolved
// segment size is 20m, each change set is 1m, so we need a bit more than 20 commits to reproduce.
for i := 0; i < 30; i++ {
_, _, err := db.Commit([]*NamedChangeSet{{Name: "test", Changeset: cs}})
require.NoError(t, err)
}

<-db.snapshotRewriteChan
require.NoError(t, db.Close())
}