diff --git a/CHANGELOG.md b/CHANGELOG.md index eb23b60027af..15046e02b0b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ * (server) [#21941](https://github.com/cosmos/cosmos-sdk/pull/21941) Regenerate addrbook.json for in place testnet. * (store) [#923](https://github.com/crypto-org-chain/cosmos-sdk/pull/923) Enable iavl async pruning. +* (store) [#934](https://github.com/crypto-org-chain/cosmos-sdk/pull/934) Add pause pruning. ### Bug Fixes diff --git a/store/iavl/store.go b/store/iavl/store.go index 8e091260fe40..7066891cda19 100644 --- a/store/iavl/store.go +++ b/store/iavl/store.go @@ -145,6 +145,15 @@ func (st *Store) LastCommitID() types.CommitID { } } +// PausePruning implements CommitKVStore interface. +func (st *Store) PausePruning(pause bool) { + if pause { + st.tree.SetCommitting() + } else { + st.tree.UnsetCommitting() + } +} + // SetPruning panics as pruning options should be provided at initialization // since IAVl accepts pruning options directly. func (st *Store) SetPruning(_ pruningtypes.PruningOptions) { diff --git a/store/iavl/tree.go b/store/iavl/tree.go index 889fc1d5a07f..f6a2db1ffc79 100644 --- a/store/iavl/tree.go +++ b/store/iavl/tree.go @@ -22,6 +22,8 @@ type ( Get(key []byte) ([]byte, error) Set(key, value []byte) (bool, error) Remove(key []byte) ([]byte, bool, error) + SetCommitting() + UnsetCommitting() SaveVersion() ([]byte, int64, error) Version() int64 Hash() []byte @@ -53,6 +55,14 @@ func (it *immutableTree) Remove(_ []byte) ([]byte, bool, error) { panic("cannot call 'Remove' on an immutable IAVL tree") } +func (it *immutableTree) SetCommitting() { + panic("cannot call 'SetCommitting' on an immutable IAVL tree") +} + +func (it *immutableTree) UnsetCommitting() { + panic("cannot call 'UnsetCommitting' on an immutable IAVL tree") +} + func (it *immutableTree) SaveVersion() ([]byte, int64, error) { panic("cannot call 'SaveVersion' on an immutable IAVL tree") } diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index bb3bc04ac4f2..540814b51415 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -464,6 +464,16 @@ func (rs *Store) LastCommitID() types.CommitID { return rs.lastCommitInfo.CommitID() } +// PausePruning temporarily pauses the pruning of all individual stores which implement +// the PausablePruner interface. +func (rs *Store) PausePruning(pause bool) { + for _, store := range rs.stores { + if pauseable, ok := store.(types.PausablePruner); ok { + pauseable.PausePruning(pause) + } + } +} + // Commit implements Committer/CommitStore. func (rs *Store) Commit() types.CommitID { var previousHeight, version int64 @@ -485,7 +495,14 @@ func (rs *Store) Commit() types.CommitID { rs.logger.Debug("commit header and version mismatch", "header_height", rs.commitHeader.Height, "version", version) } - rs.lastCommitInfo = commitStores(version, rs.stores, rs.removalMap) + func() { // ensure unpause + // set the committing flag on all stores to block the pruning + rs.PausePruning(true) + // unset the committing flag on all stores to continue the pruning + defer rs.PausePruning(false) + rs.lastCommitInfo = commitStores(version, rs.stores, rs.removalMap) + }() + rs.lastCommitInfo.Timestamp = rs.commitHeader.Time defer rs.flushMetadata(rs.db, version, rs.lastCommitInfo) diff --git a/store/rootmulti/store_test.go b/store/rootmulti/store_test.go index dcb5d8482af7..23669c82a9a0 100644 --- a/store/rootmulti/store_test.go +++ b/store/rootmulti/store_test.go @@ -637,6 +637,30 @@ func TestMultiStore_PruningRestart(t *testing.T) { require.Eventually(t, isPruned, 1*time.Second, 10*time.Millisecond, "expected error when loading pruned heights") } +var _ types.PausablePruner = &pauseableCommitKVStoreStub{} + +type pauseableCommitKVStoreStub struct { + types.CommitKVStore + pauseCalled []bool +} + +func (p *pauseableCommitKVStoreStub) PausePruning(b bool) { + p.pauseCalled = append(p.pauseCalled, b) +} + +func TestPausePruningOnCommit(t *testing.T) { + store := NewStore(dbm.NewMemDB(), log.NewNopLogger(), metrics.NewNoOpMetrics()) + store.SetPruning(pruningtypes.NewCustomPruningOptions(2, 11)) + store.MountStoreWithDB(testStoreKey1, types.StoreTypeIAVL, nil) + require.NoError(t, store.LoadLatestVersion()) + myStub := &pauseableCommitKVStoreStub{CommitKVStore: store.stores[testStoreKey1].(types.CommitKVStore)} + store.stores[testStoreKey1] = myStub + // when + store.Commit() + // then + require.Equal(t, []bool{true, false}, myStub.pauseCalled) +} + // TestUnevenStoresHeightCheck tests if loading root store correctly errors when // there's any module store with the wrong height func TestUnevenStoresHeightCheck(t *testing.T) { diff --git a/store/types/store.go b/store/types/store.go index 67bd140f5e75..cd92fbd0ca07 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -29,6 +29,14 @@ type Committer interface { GetPruning() pruningtypes.PruningOptions } +type PausablePruner interface { + // PausePruning let the pruning handler know that the store is being committed + // or not, so the handler can decide to prune or not the store. + // + // NOTE: PausePruning(true) should be called before Commit() and PausePruning(false) + PausePruning(bool) +} + // Stores of MultiStore must implement CommitStore. type CommitStore interface { Committer