Skip to content
This repository was archived by the owner on Jan 20, 2026. It is now read-only.

Commit 00d174d

Browse files
authored
[MemIAVL] Create snapshot whenever height diff exceed interval (#109)
* Snapshot should be created whenever height diff exceed interval * Fix unit test * Fix unit test * Fix lint * Add log for closing db
1 parent 37c20d5 commit 00d174d

2 files changed

Lines changed: 70 additions & 4 deletions

File tree

sc/memiavl/db.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -548,12 +548,19 @@ func (db *DB) reloadMultiTree(mtree *MultiTree) error {
548548

549549
// rewriteIfApplicable execute the snapshot rewrite strategy according to current height
550550
func (db *DB) rewriteIfApplicable(height int64) {
551-
if height%int64(db.snapshotInterval) != 0 {
551+
if db.snapshotRewriteChan != nil {
552+
return
553+
}
554+
555+
if db.snapshotInterval <= 0 || height <= 0 || height < db.MultiTree.SnapshotVersion() {
552556
return
553557
}
554558

555-
if err := db.rewriteSnapshotBackground(); err != nil {
556-
db.logger.Error("failed to rewrite snapshot in background", "err", err)
559+
// create snapshot when current height - last snapshot height > interval
560+
if height-db.MultiTree.SnapshotVersion() >= int64(db.snapshotInterval) {
561+
if err := db.rewriteSnapshotBackground(); err != nil {
562+
db.logger.Error("failed to rewrite snapshot in background", "err", err)
563+
}
557564
}
558565
}
559566

@@ -616,19 +623,22 @@ func (db *DB) rewriteSnapshotBackground() error {
616623
}
617624

618625
func (db *DB) Close() error {
626+
db.logger.Info("Closing memiavl db...")
619627
db.mtx.Lock()
620628
defer db.mtx.Unlock()
621629
errs := []error{}
622630
db.pruneSnapshotLock.Lock()
623631
defer db.pruneSnapshotLock.Unlock()
624632
// Close stream handler
633+
db.logger.Info("Closing stream handler...")
625634
if db.streamHandler != nil {
626635
err := db.streamHandler.Close()
627636
errs = append(errs, err)
628637
db.streamHandler = nil
629638
}
630639

631640
// Close rewrite channel
641+
db.logger.Info("Closing rewrite channel...")
632642
if db.snapshotRewriteChan != nil {
633643
db.snapshotRewriteCancelFunc()
634644
<-db.snapshotRewriteChan
@@ -639,12 +649,13 @@ func (db *DB) Close() error {
639649
errs = append(errs, db.MultiTree.Close())
640650

641651
// Close file lock
652+
db.logger.Info("Closing file lock...")
642653
if db.fileLock != nil {
643654
errs = append(errs, db.fileLock.Unlock())
644655
errs = append(errs, db.fileLock.Destroy())
645656
db.fileLock = nil
646657
}
647-
658+
db.logger.Info("Closed memiavl db.")
648659
return errorutils.Join(errs...)
649660
}
650661

sc/memiavl/db_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,61 @@ func TestRewriteSnapshotBackground(t *testing.T) {
137137
stopped = true
138138
}
139139

140+
// helper to commit one change to bump height
141+
func RequireCommitWithNoError(t *testing.T, db *DB, key, val string) int64 {
142+
pairs := []*iavl.KVPair{{Key: []byte(key), Value: []byte(val)}}
143+
cs := []*proto.NamedChangeSet{
144+
{Name: "test", Changeset: iavl.ChangeSet{Pairs: pairs}},
145+
}
146+
require.NoError(t, db.ApplyChangeSets(cs))
147+
v, err := db.Commit()
148+
require.NoError(t, err)
149+
return v
150+
}
151+
152+
// Ensures snapshot rewrite is triggered when current height minus last snapshot height
153+
// exceeds the configured snapshot interval (strictly greater).
154+
func TestSnapshotTriggerOnIntervalDiff(t *testing.T) {
155+
dir := t.TempDir()
156+
db, err := OpenDB(logger.NewNopLogger(), 0, Options{
157+
Dir: dir,
158+
CreateIfMissing: true,
159+
InitialStores: []string{"test"},
160+
SnapshotInterval: 5,
161+
SnapshotKeepRecent: 0,
162+
})
163+
require.NoError(t, err)
164+
165+
// Heights 1..4 should NOT trigger because diff<=interval
166+
for i := 1; i < 5; i++ {
167+
v := RequireCommitWithNoError(t, db, "k"+strconv.Itoa(i), "v")
168+
require.EqualValues(t, i, v)
169+
// allow any background processing
170+
time.Sleep(10 * time.Millisecond)
171+
require.Nil(t, db.snapshotRewriteChan, "rewrite should not start at height %d", i)
172+
// snapshot version should remain 0 until rewrite
173+
require.EqualValues(t, 0, db.MultiTree.SnapshotVersion())
174+
}
175+
176+
// Height 5 should trigger rewrite
177+
v := RequireCommitWithNoError(t, db, "k6", "v")
178+
require.Equal(t, int64(5), v)
179+
180+
// wait briefly for background rewrite to start
181+
require.Eventually(t, func() bool {
182+
return db.snapshotRewriteChan != nil
183+
}, 3*time.Second, 100*time.Millisecond)
184+
require.Eventually(t, func() bool {
185+
require.NoError(t, db.checkAsyncTasks())
186+
return db.snapshotRewriteChan == nil
187+
}, 5*time.Second, 100*time.Millisecond)
188+
189+
// After completion, snapshot version should be 6
190+
require.EqualValues(t, 5, db.MultiTree.SnapshotVersion())
191+
192+
require.NoError(t, db.Close())
193+
}
194+
140195
func TestRlog(t *testing.T) {
141196
dir := t.TempDir()
142197
db, err := OpenDB(logger.NewNopLogger(), 0, Options{

0 commit comments

Comments
 (0)