Skip to content

Commit 7576ee2

Browse files
Merge pull request #6341 from oasisprotocol/martin/feature/cli/prune-offline
go/oasis-node/cmd/storage: Add command for offline pruning
2 parents 6049247 + ce38558 commit 7576ee2

10 files changed

Lines changed: 345 additions & 28 deletions

File tree

.changelog/6321.feature.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
go/oasis-node: Add new command for offline pruning of consensus databases
2+
3+
A new experimental command `oasis-node storage prune-experimental`
4+
was added, enabling offline pruning as specified in the configuration.
5+
6+
Operators are encouraged to run this command whenever they change pruning
7+
configuration, to ensure node is healthy when it starts.

docs/oasis-node/cli.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ oasis1qqncl383h8458mr9cytatygctzwsx02n4c5f8ed7
336336

337337
### compact-experimental
338338

339-
Run
339+
Run (when the node is not running):
340340

341341
```sh
342342
oasis-node storage compact-experimental --config /path/to/config/file
@@ -364,3 +364,26 @@ may stay constant or not be reclaimed for a very long time.
364364

365365
This command gives operators manual control to release disk space during
366366
maintenance periods.
367+
368+
### prune-experimental
369+
370+
Run (when the node is not running):
371+
372+
```sh
373+
oasis-node storage prune-experimental --config /path/to/config/file
374+
```
375+
376+
to trigger manual pruning of consensus database instances:
377+
378+
```sh
379+
{"caller":"storage.go:433","level":"info","module":"cmd/storage", \
380+
"msg":"Starting consensus databases pruning. This may take a while...", \
381+
"ts":"2025-10-23T11:02:11.129822974Z"}
382+
```
383+
384+
Operators should run this whenever they change pruning configuration, e.g. when
385+
enabling it for the first time, or later changing it to retain less data. This
386+
way they guarantee the node is healthy when it starts.
387+
388+
Following successful pruning, to release disk space, they are encouraged to run
389+
[the compaction command](#compact-experimental).

go/consensus/cometbft/abci/prune.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ func (p *genericPruner) canPrune(v int64) error {
200200
return nil
201201
}
202202

203+
// Warning: When registering new handler DO NOT forget to update the logic for
204+
// "oasis-node storage prune" command as well.
203205
func (p *genericPruner) RegisterHandler(handler consensus.StatePruneHandler) {
204206
p.Lock()
205207
defer p.Unlock()

go/consensus/cometbft/db/init.go

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,61 @@
22
package db
33

44
import (
5+
"fmt"
6+
57
dbm "github.com/cometbft/cometbft-db"
8+
cmtconfig "github.com/cometbft/cometbft/config"
69
"github.com/cometbft/cometbft/node"
10+
cmtnode "github.com/cometbft/cometbft/node"
11+
"github.com/cometbft/cometbft/state"
712

813
"github.com/oasisprotocol/oasis-core/go/consensus/cometbft/db/badger"
914
)
1015

11-
// GetBackendName returns the currently configured CometBFT database backend.
12-
func GetBackendName() string {
16+
// BackendName returns the currently configured CometBFT database backend.
17+
func BackendName() string {
1318
return badger.BackendName
1419
}
1520

16-
// GetProvider returns the currently configured CometBFT DBProvider.
17-
func GetProvider() (node.DBProvider, error) {
21+
// Provider returns the currently configured CometBFT DBProvider.
22+
func Provider() (node.DBProvider, error) {
1823
return badger.DBProvider, nil
1924
}
2025

2126
// New constructs a new CometBFT DB with the configured backend.
2227
func New(fn string, noSuffix bool) (dbm.DB, error) {
2328
return badger.New(fn, noSuffix)
2429
}
30+
31+
// OpenBlockstoreDB opens a CometBFT managed blockstore DB.
32+
//
33+
// This function is a hack as CometBFT does not expose a way to access the underlying databases.
34+
func OpenBlockstoreDB(provider cmtnode.DBProvider, cfg *cmtconfig.Config) (dbm.DB, error) {
35+
// NOTE: DBContext uses a full CometBFT config but the only thing that is actually used
36+
// is the data dir field.
37+
db, err := provider(&cmtnode.DBContext{ID: "blockstore", Config: cfg})
38+
if err != nil {
39+
return nil, fmt.Errorf("failed to open blockstore: %w", err)
40+
}
41+
42+
return db, nil
43+
}
44+
45+
// OpenStateDB opens a CometBFT managed state DB.
46+
//
47+
// This function is a hack as CometBFT does not expose a way to access the underlying databases.
48+
func OpenStateDB(provider cmtnode.DBProvider, cfg *cmtconfig.Config) (dbm.DB, error) {
49+
// NOTE: DBContext uses a full CometBFT config but the only thing that is actually used
50+
// is the data dir field.
51+
db, err := provider(&cmtnode.DBContext{ID: "state", Config: cfg})
52+
if err != nil {
53+
return nil, fmt.Errorf("failed to open state db: %w", err)
54+
}
55+
56+
return db, nil
57+
}
58+
59+
// OpenStateStore constructs a new state store using default options.
60+
func OpenStateStore(stateDB dbm.DB) state.Store {
61+
return state.NewStore(stateDB, state.StoreOptions{})
62+
}

go/consensus/cometbft/full/archive.go

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@ import (
77
"sync"
88
"time"
99

10-
dbm "github.com/cometbft/cometbft-db"
1110
abcicli "github.com/cometbft/cometbft/abci/client"
1211
cmtconfig "github.com/cometbft/cometbft/config"
1312
cmtsync "github.com/cometbft/cometbft/libs/sync"
14-
cmtnode "github.com/cometbft/cometbft/node"
1513
cmtproxy "github.com/cometbft/cometbft/proxy"
1614
cmtcore "github.com/cometbft/cometbft/rpc/core"
1715
"github.com/cometbft/cometbft/state"
@@ -194,31 +192,27 @@ func NewArchive(ctx context.Context, cfg ArchiveConfig) (consensusAPI.Service, e
194192
logger := tmcommon.NewLogAdapter(!config.GlobalConfig.Consensus.LogDebug)
195193
srv.abciClient = abcicli.NewLocalClient(new(cmtsync.Mutex), srv.mux.Mux())
196194

197-
dbProvider, err := db.GetProvider()
198-
if err != nil {
199-
return nil, err
200-
}
201195
cmtConfig := cmtconfig.DefaultConfig()
202196
_ = viper.Unmarshal(&cmtConfig)
203197
cmtConfig.SetRoot(filepath.Join(srv.dataDir, tmcommon.StateDir))
204198

205-
// NOTE: DBContext uses a full CometBFT config but the only thing that is actually used
206-
// is the data dir field.
207-
srv.blockStoreDB, err = dbProvider(&cmtnode.DBContext{ID: "blockstore", Config: cmtConfig})
199+
dbProvider, err := db.Provider()
200+
if err != nil {
201+
return nil, fmt.Errorf("failed to obtain db provider: %w", err)
202+
}
203+
204+
srv.blockStoreDB, err = db.OpenBlockstoreDB(dbProvider, cmtConfig)
208205
if err != nil {
209206
return nil, err
210207
}
211208
srv.blockStoreDB = db.WithCloser(srv.blockStoreDB, srv.dbCloser)
212209

213-
// NOTE: DBContext uses a full CometBFT config but the only thing that is actually used
214-
// is the data dir field.
215-
var stateDB dbm.DB
216-
stateDB, err = dbProvider(&cmtnode.DBContext{ID: "state", Config: cmtConfig})
210+
stateDB, err := db.OpenStateDB(dbProvider, cmtConfig)
217211
if err != nil {
218212
return nil, err
219213
}
220214
stateDB = db.WithCloser(stateDB, srv.dbCloser)
221-
srv.stateStore = state.NewStore(stateDB, state.StoreOptions{})
215+
srv.stateStore = db.OpenStateStore(stateDB)
222216

223217
srv.eb = cmttypes.NewEventBus()
224218
// Setup minimal CometBFT environment needed to support consensus queries.

go/consensus/cometbft/full/full.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ func (t *fullService) lazyInit() error { // nolint: gocyclo
668668
return t.genesisDoc, nil
669669
}
670670

671-
dbProvider, err := db.GetProvider()
671+
dbProvider, err := db.Provider()
672672
if err != nil {
673673
t.Logger.Error("failed to obtain database provider",
674674
"err", err,

go/oasis-node/cmd/common/common.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
package common
33

44
import (
5+
"errors"
56
"fmt"
67
"io"
8+
"io/fs"
79
"os"
810
"path/filepath"
911
"strings"
@@ -71,6 +73,20 @@ func InternalSocketPath() string {
7173
return filepath.Join(DataDir(), InternalSocketName)
7274
}
7375

76+
// IsNodeRunning returns true when the node is running.
77+
func IsNodeRunning() (bool, error) {
78+
path := InternalSocketPath()
79+
80+
if _, err := os.Stat(path); err != nil {
81+
if errors.Is(err, fs.ErrNotExist) {
82+
return false, nil
83+
}
84+
return false, fmt.Errorf("stat %s: %w", path, err)
85+
}
86+
87+
return true, nil
88+
}
89+
7490
// IsNodeCmd returns true iff the current command is the ekiden node.
7591
func IsNodeCmd() bool {
7692
return isNodeCmd

0 commit comments

Comments
 (0)