Skip to content

Commit a9c71e7

Browse files
committed
prunning
1 parent c4586e3 commit a9c71e7

File tree

3 files changed

+122
-0
lines changed

3 files changed

+122
-0
lines changed

pkg/adapter/adapter.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,19 @@ type Adapter struct {
9090
stackedEvents []StackedEvent
9191
}
9292

93+
// PruneExecMeta implements execution.ExecMetaPruner for the ABCI adapter by
94+
// delegating to the underlying ev-abci exec store. It prunes per-height ABCI
95+
// execution metadata (block IDs and block responses) up to the given height.
96+
// The method is safe to call multiple times with the same or increasing
97+
// heights.
98+
func (a *Adapter) PruneExecMeta(ctx context.Context, height uint64) error {
99+
if a.Store == nil {
100+
return nil
101+
}
102+
103+
return a.Store.Prune(ctx, height)
104+
}
105+
93106
// NewABCIExecutor creates a new Adapter instance that implements the go-execution.Executor interface.
94107
// The Adapter wraps the provided ABCI application and delegates execution-related operations to it.
95108
func NewABCIExecutor(

pkg/store/store.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package store
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"strconv"
78

@@ -24,6 +25,10 @@ const (
2425
blockResponseKey = "br"
2526
// blockIDKey is the key used for storing block IDs
2627
blockIDKey = "bid"
28+
// lastPrunedHeightKey tracks the highest height that has been pruned.
29+
// This makes pruning idempotent and allows incremental pruning across
30+
// multiple calls.
31+
lastPrunedHeightKey = "lph"
2732
)
2833

2934
// Store wraps a datastore with ABCI-specific functionality
@@ -147,3 +152,54 @@ func (s *Store) GetBlockResponse(ctx context.Context, height uint64) (*abci.Resp
147152

148153
return resp, nil
149154
}
155+
156+
// Prune deletes per-height ABCI execution metadata (block IDs and block
157+
// responses) for all heights up to and including the provided target
158+
// height. The current ABCI state (stored under stateKey) is never pruned,
159+
// as it is maintained separately by the application.
160+
//
161+
// Pruning is idempotent: the store tracks the highest pruned height and
162+
// will skip work for already-pruned ranges.
163+
func (s *Store) Prune(ctx context.Context, height uint64) error {
164+
// Load the last pruned height, if any.
165+
data, err := s.prefixedStore.Get(ctx, ds.NewKey(lastPrunedHeightKey))
166+
if err != nil {
167+
if !errors.Is(err, ds.ErrNotFound) {
168+
return fmt.Errorf("failed to get last pruned height: %w", err)
169+
}
170+
}
171+
172+
var lastPruned uint64
173+
if len(data) > 0 {
174+
lastPruned, err = strconv.ParseUint(string(data), 10, 64)
175+
if err != nil {
176+
return fmt.Errorf("invalid last pruned height value %q: %w", string(data), err)
177+
}
178+
}
179+
180+
// Nothing to do if we've already pruned up to at least this height.
181+
if height <= lastPruned {
182+
return nil
183+
}
184+
185+
// Delete per-height ABCI metadata (block IDs and block responses) for
186+
// heights in (lastPruned, height]. Missing keys are ignored.
187+
for h := lastPruned + 1; h <= height; h++ {
188+
bidKey := ds.NewKey(blockIDKey).ChildString(strconv.FormatUint(h, 10))
189+
if err := s.prefixedStore.Delete(ctx, bidKey); err != nil && !errors.Is(err, ds.ErrNotFound) {
190+
return fmt.Errorf("failed to delete block ID at height %d during pruning: %w", h, err)
191+
}
192+
193+
brKey := ds.NewKey(blockResponseKey).ChildString(strconv.FormatUint(h, 10))
194+
if err := s.prefixedStore.Delete(ctx, brKey); err != nil && !errors.Is(err, ds.ErrNotFound) {
195+
return fmt.Errorf("failed to delete block response at height %d during pruning: %w", h, err)
196+
}
197+
}
198+
199+
// Persist the updated last pruned height.
200+
if err := s.prefixedStore.Put(ctx, ds.NewKey(lastPrunedHeightKey), []byte(strconv.FormatUint(height, 10))); err != nil {
201+
return fmt.Errorf("failed to update last pruned height: %w", err)
202+
}
203+
204+
return nil
205+
}

pkg/store/store_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,56 @@ func TestStateIO(t *testing.T) {
2222
require.NoError(t, gotErr)
2323
assert.True(t, exists)
2424
}
25+
26+
func TestPrune_RemovesPerHeightABCIKeysUpToTarget(t *testing.T) {
27+
db := ds.NewMapDatastore()
28+
absciStore := store.NewExecABCIStore(db)
29+
30+
ctx := t.Context()
31+
32+
// Seed per-height block ID and block response keys for heights 1..5.
33+
for h := 1; h <= 5; h++ {
34+
heightKey := ds.NewKey("/abci/bid").ChildString(string(rune(h + '0')))
35+
require.NoError(t, db.Put(ctx, heightKey, []byte("bid")))
36+
37+
respKey := ds.NewKey("/abci/br").ChildString(string(rune(h + '0')))
38+
require.NoError(t, db.Put(ctx, respKey, []byte("br")))
39+
}
40+
41+
// Seed state to ensure it is not affected by pruning.
42+
require.NoError(t, db.Put(ctx, ds.NewKey("/abci/s"), []byte("state")))
43+
44+
// Prune up to height 3.
45+
require.NoError(t, absciStore.Prune(ctx, 3))
46+
47+
// Heights 1..3 should be deleted.
48+
for h := 1; h <= 3; h++ {
49+
bidKey := ds.NewKey("/abci/bid").ChildString(string(rune(h + '0')))
50+
exists, err := db.Has(ctx, bidKey)
51+
require.NoError(t, err)
52+
assert.False(t, exists)
53+
54+
respKey := ds.NewKey("/abci/br").ChildString(string(rune(h + '0')))
55+
exists, err = db.Has(ctx, respKey)
56+
require.NoError(t, err)
57+
assert.False(t, exists)
58+
}
59+
60+
// Heights 4..5 should remain.
61+
for h := 4; h <= 5; h++ {
62+
bidKey := ds.NewKey("/abci/bid").ChildString(string(rune(h + '0')))
63+
exists, err := db.Has(ctx, bidKey)
64+
require.NoError(t, err)
65+
assert.True(t, exists)
66+
67+
respKey := ds.NewKey("/abci/br").ChildString(string(rune(h + '0')))
68+
exists, err = db.Has(ctx, respKey)
69+
require.NoError(t, err)
70+
assert.True(t, exists)
71+
}
72+
73+
// State should not be pruned.
74+
exists, err := db.Has(ctx, ds.NewKey("/abci/s"))
75+
require.NoError(t, err)
76+
assert.True(t, exists)
77+
}

0 commit comments

Comments
 (0)