-
Notifications
You must be signed in to change notification settings - Fork 887
Add giga config and unify garbage collector logic #3829
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
326e505
01683ce
883a7a9
e1b473a
1d3c180
74629a0
f650195
c274866
9d8f9be
f623054
f386ed4
dad24c1
0c00022
3560b15
d297559
ef7d7fe
6cfcc2c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| package config | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| "github.com/sei-protocol/sei-chain/sei-db/common/utils" | ||
| "github.com/sei-protocol/sei-chain/sei-db/ledger_db/block/littblock" | ||
| "github.com/sei-protocol/sei-chain/sei-db/management/gc" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nit] This makes The stated motivation — one source of truth for |
||
| flatkvConfig "github.com/sei-protocol/sei-chain/sei-db/state_db/sc/flatkv/config" | ||
| ) | ||
|
|
||
| // GigaStorageConfig is an in-process composition of store configs for Giga storage. | ||
| // It is intentionally not read from app.toml (no mapstructure tags, no TOML section): | ||
| // callers build it via DefaultGigaStorageConfig. Nested knobs live on the store and | ||
| // collector configs they already own (StateStoreConfig, ReceiptStoreConfig, | ||
| // gc.StorageGarbageCollectorConfig) rather than being redeclared here — in particular | ||
| // RollbackWindow has a single source of truth in | ||
| // gc.DefaultStorageGarbageCollectorConfig; per-store extras live on each | ||
| // PrunableStore via GetRetentionWindow. | ||
| type GigaStorageConfig struct { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] Every other config struct in this package ( |
||
| HomePath string | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nit] Two small gaps vs. the sibling configs in this package:
|
||
| FlatKVConfig *flatkvConfig.Config | ||
| SSConfig StateStoreConfig | ||
| ReceiptDBConfig ReceiptStoreConfig | ||
| BlockDBConfig *littblock.LittBlockConfig | ||
| PruningConfig *gc.StorageGarbageCollectorConfig | ||
| } | ||
|
|
||
| // DefaultGigaStorageConfig returns a GigaStorageConfig whose store directories match the | ||
| // layout below, and whose pruning knobs are gc.DefaultStorageGarbageCollectorConfig(): | ||
| // | ||
| // data/state_commit/flatkv | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nit] This layout holds for new nodes only — |
||
| // data/state_store/evm/{backend} (sole SS; no Cosmos SS in Giga) | ||
| // data/ledger/receipt/{backend} | ||
| // data/ledger/block | ||
| // | ||
| // Giga opens SS via evm.NewEVMStateStore(dir, ssConfig) directly — not through | ||
| // composite.NewCompositeStateStore — so the EVM path lives on EVMDBDirectory (the | ||
| // argument callers pass as dir). DBDirectory and EVMSplit are left at their defaults: | ||
| // they only matter for the composite path, which Giga does not use. | ||
| // | ||
| // Unlike the other Default*Config helpers in this package, this can fail: the block-store | ||
| // default wraps littdb.DefaultConfig, which validates the directory path. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nit] This comment isn't accurate: |
||
| func DefaultGigaStorageConfig(homePath string) (GigaStorageConfig, error) { | ||
|
yzang2019 marked this conversation as resolved.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] This constructor has no test coverage. A table test asserting each resolved directory equals the corresponding |
||
| blockDBConfig, err := littblock.DefaultConfig(utils.GetBlockStorePath(homePath)) | ||
| if err != nil { | ||
| return GigaStorageConfig{}, fmt.Errorf("failed to build block db config: %w", err) | ||
| } | ||
|
|
||
| flatKV := flatkvConfig.DefaultConfig() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] The rollback invariant still holds (a snapshot at or below
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will be fixed in the future PR, right now it's not wired up yet |
||
| flatKV.DataDir = utils.GetFlatKVPath(homePath) | ||
|
|
||
| ssConfig := DefaultStateStoreConfig() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] Codex flagged this as a live double-pruner; it is currently latent, but worth zeroing anyway. Since the doc comment above advertises this config as the pruning authority, setting
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will take care in future PRs |
||
| ssConfig.EVMDBDirectory = utils.GetEVMStateStorePath(homePath, ssConfig.Backend) | ||
|
|
||
| receiptConfig := DefaultReceiptStoreConfig() | ||
| receiptConfig.DBDirectory = utils.GetReceiptStorePath(homePath, receiptConfig.Backend) | ||
|
|
||
| return GigaStorageConfig{ | ||
| HomePath: homePath, | ||
| FlatKVConfig: flatKV, | ||
| SSConfig: ssConfig, | ||
| ReceiptDBConfig: receiptConfig, | ||
| BlockDBConfig: blockDBConfig, | ||
| PruningConfig: gc.DefaultStorageGarbageCollectorConfig(), | ||
| }, nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package config | ||
|
|
||
| import ( | ||
| "path/filepath" | ||
| "testing" | ||
|
|
||
| "github.com/stretchr/testify/require" | ||
|
|
||
| "github.com/sei-protocol/sei-chain/sei-db/common/utils" | ||
| ) | ||
|
|
||
| // TestDefaultGigaStorageConfigDirectories pins Giga's EVM-only SS layout: the path | ||
| // for evm.NewEVMStateStore lives on EVMDBDirectory. Giga does not use composite, so | ||
| // DBDirectory stays empty and EVMSplit stays at its default. | ||
| func TestDefaultGigaStorageConfigDirectories(t *testing.T) { | ||
| home := t.TempDir() | ||
| cfg, err := DefaultGigaStorageConfig(home) | ||
| require.NoError(t, err) | ||
|
|
||
| require.Equal(t, home, cfg.HomePath) | ||
| require.False(t, cfg.SSConfig.EVMSplit) | ||
| require.Empty(t, cfg.SSConfig.DBDirectory) | ||
| require.Equal(t, utils.GetEVMStateStorePath(home, cfg.SSConfig.Backend), cfg.SSConfig.EVMDBDirectory) | ||
|
|
||
| require.Equal(t, utils.GetFlatKVPath(home), cfg.FlatKVConfig.DataDir) | ||
| require.Equal(t, utils.GetReceiptStorePath(home, cfg.ReceiptDBConfig.Backend), cfg.ReceiptDBConfig.DBDirectory) | ||
| require.NotNil(t, cfg.BlockDBConfig.Litt) | ||
| require.Equal(t, []string{utils.GetBlockStorePath(home)}, cfg.BlockDBConfig.Litt.Paths) | ||
|
|
||
| require.Equal(t, | ||
| filepath.Join(home, "data", "state_store", "evm", cfg.SSConfig.Backend), | ||
| cfg.SSConfig.EVMDBDirectory, | ||
| ) | ||
| } |
This file was deleted.
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package gc | ||
|
|
||
| // InfiniteRetentionWindow is the conventional GetRetentionWindow value meaning "never | ||
| // prune this store." Any negative value is treated the same: the store does not vote | ||
| // and is never passed to PruneBelow. | ||
| // | ||
| // Note: elsewhere in this repo (KeepRecent / MinRetainBlocks) 0 often means "keep | ||
| // forever." Here 0 means "no extra beyond the shared RollbackWindow," so infinite | ||
| // retention needs a negative sentinel. | ||
| const InfiniteRetentionWindow int64 = -1 | ||
|
|
||
| // PrunableStore is a store whose old data may be dropped by the StorageGarbageCollector. | ||
| type PrunableStore interface { | ||
| // Name identifies the store (logging / errors). Duplicate names are allowed; | ||
| // the collector keys answers by index, not by name. | ||
| Name() string | ||
|
|
||
| // PruneBelow may drop data for all blocks below blockNumber. | ||
| // The store may perform the deletion asynchronously. | ||
| PruneBelow(blockNumber uint64) error | ||
|
|
||
| // GetRetentionWindow is how many extra blocks beyond the shared RollbackWindow | ||
| // this store needs to keep servable. Cut line (head = min non-zero GetLatestBlock): | ||
| // | ||
| // retention < 0 → store ignored (cutLine treated as 0; conventionally -1) | ||
| // retention >= 0 → cutLine = head - RollbackWindow - retention | ||
| // | ||
| // RollbackWindow is shared so every store stays consistent under rollback. | ||
| // Retention is optional history on top — kept so the store can still serve queries | ||
| // after a rollback that consumes the entire rollback window. | ||
| // | ||
| // Because the collector prunes every participating store to the *lowest* | ||
| // GetPruningBoundary, a large retention on one store effectively deepens pruning | ||
| // for the others (see StorageGarbageCollector). Read this as an input to that | ||
| // shared min, not as an independently applied per-store policy. | ||
| // | ||
| // Contract by store kind: | ||
| // - Contiguous (blockDB, receiptDB, state WAL): -1 / 0 / positive as configured. | ||
| // - SC: always 0 (only needs the shared rollback window for snapshots). | ||
| // - SS: always 0 (SS prunes its own version history via KeepRecent; the collector | ||
| // only drives SS snapshot pruning for the shared rollback window). | ||
| GetRetentionWindow() int64 | ||
|
|
||
| // GetPruningBoundary returns the oldest block this store must keep to remain able to | ||
| // serve cutLine, or 0 to opt out of this cycle (not participate in calculating min prune height). | ||
| // | ||
| // Contiguous stores can restore to any height they hold → return cutLine. | ||
| // | ||
| // Snapshot stores can restore only at snapshot heights → return the newest completed | ||
| // snapshot ≤ cutLine, or the oldest completed snapshot if every snapshot is above | ||
| // cutLine (none can be dropped yet). No completed snapshot → 0. | ||
| // | ||
| // Assumption: snapshot creation finishes quickly enough that an in-flight write need | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] This assumption is a deliberate weakening of the base behavior and deserves to be called out as such rather than only as "out of scope". The code being deleted skipped the entire cycle when any store was empty, with the comment "a snapshot may be mid-write and take hours" — exactly the case now declared out of scope. Concrete degradation: a snapshot store with no completed snapshot answers The stated invariant ("if rollback was possible before the prune, the prune does not take it away") does survive, since rollback was already impossible with no snapshot — so this isn't a blocker. But the window is materially larger than "seconds" once |
||
| // not be reserved; modeling long-running snapshot writes is out of scope. | ||
| GetPruningBoundary(cutLine uint64) uint64 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [suggestion] The "no completed snapshot → return 0" rule drops a protection the deleted code had on purpose. Previously an empty snapshot store aborted the whole cycle, with the reason spelled out inline: "An empty store is treated as unknown rather than empty (a snapshot may be mid-write and take hours)." Now the other stores prune freely, so a WAL can be pruned to The PR body scopes this out on the assumption that snapshot writes complete within seconds, which is a reasonable call. The gap is that the interface offers no escape hatch for an implementer who is in that state: returning 0 here lets others prune, and returning |
||
|
|
||
| // GetLatestBlock returns the highest block this store has ingested. | ||
| // | ||
| // 0 means "nothing ingested yet" and is ignored when computing the global head, so a store | ||
| // still filling does not stall pruning for the rest of the fleet. It does not mean | ||
| // "disabled": a store disabled for this node is not instantiated and never reaches the | ||
| // collector. See StorageGarbageCollector for why skipping a 0 head is safe. | ||
| GetLatestBlock() (uint64, error) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] "the block store has only ever lived at this path" doesn't match the tree: the only production block store today is autobahn's littblock, opened at
filepath.Join(dir, "blockdb")underpersistent_state_dir(sei-tendermint/node/setup.go:399), notdata/ledger/block. Nothing breaks now sinceGetBlockStorePathhas no callers outsideDefaultGigaStorageConfig, but the comment will mislead whoever wires this up, and the migration story from the existingblockdbdirectory needs an answer before then.