11package committee
22
33import (
4+ "context"
45 "fmt"
6+ "time"
57
68 "github.com/oasisprotocol/oasis-core/go/common/logging"
9+ "github.com/oasisprotocol/oasis-core/go/runtime/history"
710 mkvsDB "github.com/oasisprotocol/oasis-core/go/storage/mkvs/db/api"
811)
912
@@ -12,8 +15,10 @@ type pruneHandler struct {
1215 worker * Worker
1316}
1417
15- func (p * pruneHandler ) Prune (rounds []uint64 ) error {
16- // Make sure we never prune past what was synced.
18+ // CanPruneRuntime returns no error when pruning would not go past last synced round.
19+ //
20+ // Implements runtime.history.PruneHandler.
21+ func (p * pruneHandler ) CanPruneRuntime (rounds []uint64 ) error {
1722 lastSycnedRound , _ , _ := p .worker .GetLastSynced ()
1823
1924 for _ , round := range rounds {
@@ -24,23 +29,68 @@ func (p *pruneHandler) Prune(rounds []uint64) error {
2429 }
2530
2631 // Old suggestion: Make sure we don't prune rounds that need to be checkpointed but haven't been yet.
32+ }
2733
28- p .logger .Debug ("pruning storage for round" , "round" , round )
34+ return nil
35+ }
2936
30- // Prune given block.
31- err := p .worker .localStorage .NodeDB ().Prune (round )
32- switch err {
33- case nil :
34- case mkvsDB .ErrNotEarliest :
35- p .logger .Debug ("skipping non-earliest round" ,
36- "round" , round ,
37- )
38- continue
39- default :
40- p .logger .Error ("failed to prune block" ,
41- "err" , err ,
42- )
43- return err
37+ // statePruner handles pruning of the runtime state.
38+ //
39+ // Everytime pruning is triggered, the pruner removes rounds that are older than the earliest
40+ // round in the runtime’s history.
41+ //
42+ // TODO: Pruning logic is not robust as developer changing the pruning of the runtime history
43+ // may also unexpectedly change the pruning behavior of the state db. This could be fixed
44+ // by making the storage committee worker responsible for both syncing and pruning of the
45+ // history and state DB. See https://github.com/oasisprotocol/oasis-core/issues/6400.
46+ type statePruner struct {
47+ state mkvsDB.NodeDB
48+ history history.History
49+ interval time.Duration
50+ logger * logging.Logger
51+ }
52+
53+ // newPruner creates new runtime state pruner.
54+ func newPruner (ndb mkvsDB.NodeDB , history history.History , interval time.Duration ) * statePruner {
55+ return & statePruner {
56+ state : ndb ,
57+ history : history ,
58+ interval : max (interval , time .Second ),
59+ logger : logging .GetLogger ("/worker/storage/state-pruner" ).With ("runtime_id" , history .RuntimeID ()),
60+ }
61+ }
62+
63+ // serve periodically triggers the pruning of the runtime state db.
64+ func (p * statePruner ) serve (ctx context.Context ) error {
65+ p .logger .Info ("starting" )
66+ defer p .logger .Info ("stopped" )
67+
68+ ticker := time .NewTicker (p .interval )
69+ defer ticker .Stop ()
70+
71+ for {
72+ select {
73+ case <- ctx .Done ():
74+ return ctx .Err ()
75+ case <- ticker .C :
76+ }
77+
78+ if err := p .prune (ctx ); err != nil {
79+ p .logger .Warn ("failed to prune" , "err" , err )
80+ }
81+ }
82+ }
83+
84+ func (p * statePruner ) prune (ctx context.Context ) error {
85+ blk , err := p .history .GetEarliestBlock (ctx )
86+ if err != nil {
87+ return fmt .Errorf ("failed to get earliest block from runtime history: %w" , err )
88+ }
89+
90+ for round := p .state .GetEarliestVersion (); round < blk .Header .Round ; round ++ {
91+ p .logger .Debug ("pruning" , "round" , round )
92+ if err := p .state .Prune (round ); err != nil {
93+ return fmt .Errorf ("failed to prune round %d: %w" , round , err )
4494 }
4595 }
4696
0 commit comments