Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changelog/6519.feature.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
go/oasis-node/cmd/storage: Release offline pruning command

Command `storage prune-experimental` has been released as
`storage prune` and now in addition to consensus databases
also prunes runtime databases.
5 changes: 5 additions & 0 deletions .changelog/6519.feature.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
go/oasis-node/cmd/storage: Release offline compaction command

Command `storage compact-experimental` has been released as
`storage compact` and now in addition to consensus databases
also compacts runtime databases.
20 changes: 10 additions & 10 deletions docs/oasis-node/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,15 @@ oasis1qqncl383h8458mr9cytatygctzwsx02n4c5f8ed7

## storage

### compact-experimental
### compact

Run (when the node is not running):
Comment thread
martintomazic marked this conversation as resolved.

```sh
oasis-node storage compact-experimental --config /path/to/config/file
oasis-node storage compact --config /path/to/config/file
```

to trigger manual compaction of consensus database instances:
to trigger manual compaction of all configured database instances:

```sh
{"caller":"storage.go:310","level":"info","module":"cmd/storage", \
Expand All @@ -365,28 +365,28 @@ may stay constant or not be reclaimed for a very long time.
This command gives operators manual control to release disk space during
maintenance periods.

### prune-experimental
### prune

Run (when the node is not running):

```sh
oasis-node storage prune-experimental --config /path/to/config/file
oasis-node storage prune --config /path/to/config/file
```

to trigger manual pruning of consensus database instances:
to trigger manual pruning of all configured database instances:

```sh
{"caller":"storage.go:433","level":"info","module":"cmd/storage", \
"msg":"Starting consensus databases pruning. This may take a while...", \
{"caller":"prune.go:47","level":"info","module":"cmd/storage", \
"msg":"Starting databases pruning. This may take a while...", \
"ts":"2025-10-23T11:02:11.129822974Z"}
```

Operators should run this whenever they change pruning configuration, e.g. when
enabling it for the first time, or later changing it to retain less data. This
way they guarantee the node is healthy when it starts.

Following successful pruning, to release disk space, they are encouraged to run
[the compaction command](#compact-experimental).
After the pruning operators should run [the compaction command](#compact)
to release disk space.

### inspect

Expand Down
153 changes: 153 additions & 0 deletions go/oasis-node/cmd/storage/compact.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package storage

import (
"fmt"
"io/fs"
"path/filepath"
"strings"

"github.com/spf13/cobra"

"github.com/oasisprotocol/oasis-core/go/common"
cmtDBProvider "github.com/oasisprotocol/oasis-core/go/consensus/cometbft/db/badger"
cmdCommon "github.com/oasisprotocol/oasis-core/go/oasis-node/cmd/common"
"github.com/oasisprotocol/oasis-core/go/runtime/registry"
)

func newCompactCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "compact",
Args: cobra.NoArgs,
Short: "trigger compaction for all databases",
Long: `Optimize the storage for all databases by manually compacting the underlying storage engines.

WARNING: Ensure you have at least as much of a free disk as your largest database.
`,
PreRunE: func(_ *cobra.Command, args []string) error {
if err := cmdCommon.Init(); err != nil {
cmdCommon.EarlyLogAndExit(err)
}

running, err := cmdCommon.IsNodeRunning()
if err != nil {
return fmt.Errorf("failed to ensure the node is not running: %w", err)
}

if running {
return fmt.Errorf("compaction can only be done when the node is not running")
}
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
dataDir := cmdCommon.DataDir()

logger.Info("Starting database compactions. This may take a while...")

if err := compactConsensusDBs(dataDir); err != nil {
return fmt.Errorf("failed to compact consensus databases: %w", err)
}

runtimes, err := registry.GetConfiguredRuntimeIDs()
if err != nil {
return fmt.Errorf("failed to get configured runtimes: %w", err)
}
for _, rt := range runtimes {
if err := compactRuntimeDBs(dataDir, rt); err != nil {
return fmt.Errorf("failed to compact runtime dbs (runtime ID: %s): %w", rt, err)
}
}

return nil
},
}

return cmd
}

func compactConsensusDBs(dataDir string) error {
// Compact CometBFT managed databases: block store, evidence and state (NOT application state).
if err := compactCometDBs(dataDir); err != nil {
return fmt.Errorf("failed to compact CometBFT managed databases: %w", err)
}

// Compact consensus NodeDB (application state).
ndb, close, err := openConsensusNodeDB(dataDir)
if err != nil {
return fmt.Errorf("failed to open consensus NodeDB: %w", err)
}
defer close()

if err := ndb.Compact(); err != nil {
return fmt.Errorf("failed to compact consensus node DB: %w", err)
}

return nil
}

func compactCometDBs(dataDir string) error {
dir := fmt.Sprintf("%s/consensus/data", dataDir)

var dbDirs []string
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() && strings.HasSuffix(d.Name(), ".db") {
dbDirs = append(dbDirs, path)
}
return nil
})
if err != nil {
return fmt.Errorf("failed to walk dir %s: %w", dir, err)
}

if len(dbDirs) == 0 {
return fmt.Errorf("zero database instances found")
}

for _, dbDir := range dbDirs {
if err := compactCometDB(dbDir); err != nil {
return fmt.Errorf("failed to compact %s: %w", dbDir, err)
}
}
return nil
}

func compactCometDB(path string) error {
logger := logger.With("path", path)
db, err := cmtDBProvider.OpenBadger(path, logger)
if err != nil {
return fmt.Errorf("failed to open BadgerDB: %w", err)
}
defer db.Close()

logger.Info("compacting")

if err := db.Flatten(1); err != nil {
return fmt.Errorf("failed to flatten db: %w", err)
}

logger.Info("compaction completed")

return nil
}

func compactRuntimeDBs(dataDir string, rt common.Namespace) error {
history, err := openRuntimeLightHistory(dataDir, rt)
if err != nil {
return fmt.Errorf("failed to open runtime history: %w", err)
}
defer history.Close()
if err := history.Compact(); err != nil {
return fmt.Errorf("failed to compact runtime history: %w", err)
}
ndb, err := openRuntimeStateDB(dataDir, rt)
if err != nil {
return fmt.Errorf("failed to open runtime state DB: %w", err)
}
defer ndb.Close()
if err := ndb.Compact(); err != nil {
return fmt.Errorf("failed to compact runtime state DB: %w", err)
}
return nil
}
Loading
Loading