backfill config loader + validation pipeline (#684)#701
backfill config loader + validation pipeline (#684)#701karthikiyer56 wants to merge 5 commits intokarthik/backfill-impl/scaffold-subcommandfrom
Conversation
Ports pkg/geometry verbatim from reference commit 19a9179 (chunk/range boundary math) and fills in the full-history-backfill subcommand body: - Config package under cmd/stellar-rpc/internal/fullhistory/backfill/config/ with UPPER_SNAKE_CASE TOML schema ([SERVICE], [BACKFILL], [BACKFILL.BSB], [IMMUTABLE_STORAGE.*], [LOGGING], [META_STORE]). - ParseConfig / MarshalTOML round-trip via pelletier/go-toml v1. - Validate (TOML struct-level rules + path-default resolution). - CLIFlags + ApplyFlags (chunk-boundary widening via pkg/geometry, workers/max-retries/logging defaults + overrides). - ValidateFlags (start/end/workers/max-retries sanity). - ValidateAgainstConfigStore (CHUNKS_PER_TXHASH_INDEX immutability via a narrow ConfigStore interface; real RocksDB-backed impl lands in #689). - ValidateAgainstBSB (availability probe via a narrow BSBAvailabilityProbe interface; real impl lands in #688). - Subcommand runBackfill: load TOML -> Validate -> ApplyFlags -> ValidateFlags -> ValidateAgainstConfigStore(stub) -> ValidateAgainstBSB(stub) -> printSummary. Adds --log-level and --log-format flags alongside the six registered in #699.
There was a problem hiding this comment.
Pull request overview
Implements the full-history backfill subcommand’s config loading + pre-DAG validation pipeline, including a new TOML-backed config schema and a ported geometry helper package used for chunk/range boundary math.
Changes:
- Added
cmd/stellar-rpc/internal/fullhistory/backfill/config/with TOML parse/marshal, validation, CLI flag merge (including chunk-boundary expansion), and validation against stubbed ConfigStore/BSB probe interfaces. - Implemented
full-history-backfillsubcommand flow: read config → validate → apply flags → validate flags → validate against config-store stub → validate against BSB stub → print summary. - Ported
pkg/geometry(plus tests) to support chunk/range/index boundary calculations.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| cmd/stellar-rpc/internal/fullhistory/pkg/geometry/geometry.go | Adds geometry constants and parameterized boundary math helpers. |
| cmd/stellar-rpc/internal/fullhistory/pkg/geometry/geometry_test.go | Unit tests for range/chunk/index math. |
| cmd/stellar-rpc/internal/fullhistory/backfill/config/config.go | Defines TOML schema + ParseConfig/MarshalTOML. |
| cmd/stellar-rpc/internal/fullhistory/backfill/config/config_test.go | Tests schema decode, defaults, and TOML round-trip. |
| cmd/stellar-rpc/internal/fullhistory/backfill/config/flags.go | CLIFlags + ApplyFlags and BuildGeometry wiring. |
| cmd/stellar-rpc/internal/fullhistory/backfill/config/flags_test.go | Tests chunk-boundary widening and flag-derived defaults. |
| cmd/stellar-rpc/internal/fullhistory/backfill/config/validate.go | Config validation, flag validation, config-store immutability, and BSB availability checks. |
| cmd/stellar-rpc/internal/fullhistory/backfill/config/validate_test.go | Tests validation rules (pass/fail) and interface-based validators. |
| cmd/stellar-rpc/internal/fullhistory/backfill/config/interfaces.go | Defines ConfigStore/BSBAvailabilityProbe + in-memory stubs. |
| cmd/stellar-rpc/internal/fullhistory/backfill/cmd.go | Wires the end-to-end subcommand pipeline and prints a resolved-config summary. |
| cmd/stellar-rpc/internal/fullhistory/backfill/cmd_test.go | Subcommand-level tests for flag registration, success path summary, and failure cases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Rename Range-era nomenclature in pkg/geometry to Index-only per the current design doc. Drop the Range* constants + methods; keep Chunk* and Index* APIs. Add LedgerToChunkID so flags.go stops inlining the formula. - Rename ConfigStore -> Store (revive: stuttering package.Type). - Rename Stub* -> InMemory* / Nop* to describe what the impl IS rather than what slice it anticipates. Forward-looking comments explain why the in-memory Store and no-op BSB probe are the right shape until #689 and #688 land their RocksDB- and GCS-backed impls. - Trim comments: keep business-logic and stub-context explanations; drop section dividers, restatement of code, and verbose godoc. - Treat --workers=0 as valid (the documented GOMAXPROCS sentinel matches cobra's flag default). Previous ValidateFlags rejected 0 as < 1, which contradicted the help text. - Fix META_STORE default-path comment casing (lowercase, matching filepath.Join literal). - TestNewCmd_MissingConfigFile now uses a t.TempDir()-derived path to remove flakiness against a pre-existing /tmp file. - golangci-lint v2.11.3 clean: errors.New over fmt.Errorf for plain strings (perfsprint), range-over-int loops (intrange), long-line split (lll), remove duplicate package docstrings (godoclint).
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- ApplyFlags no longer silently rewrites --max-retries=0 to 3. cobra already defaults the flag to 3, so omission works naturally; an explicit --max-retries=0 is a legitimate "no retries, fail fast" request and must not be clobbered. Drop the defaultMaxRetries constant and the sentinel branch. - ValidateAgainstStore now enforces its Validate()-ran-first precondition explicitly. A caller who skips Validate would otherwise have ChunksPerTxHashIndex == 0 and seed "0" into the store, permanently locking the layout to that bogus value.
| package geometry | ||
|
|
||
| // FirstLedger is the first ledger of the Stellar network. | ||
| const FirstLedger uint32 = 2 |
There was a problem hiding this comment.
Two concepts to internalize before reading this file — Chunk (fixed 10,000 ledgers, the atomic unit of ingestion + crash recovery) and Index (ChunksPerTxHashIndex chunks; the cadence at which one RecSplit txhash index gets built). All backfill math in this package is expressible through those two terms. FirstLedger=2 because Stellar ledger sequences start there — every boundary formula offsets by that anchor.
| // non-empty values. | ||
| // | ||
| // Precondition: Validate has run, so ChunksPerTxHashIndex is populated. | ||
| func (c *Config) ApplyFlags(flags CLIFlags) { |
There was a problem hiding this comment.
Chunk-boundary expansion is outward only: start widens DOWN to the first ledger of its chunk, end widens UP to the last. Never narrows. This lets operators pass any range (e.g. --start-ledger 5_000_000 --end-ledger 56_337_842) without hand-computing chunk alignment — we always cover at least the requested range, possibly slightly more at each end. The >= FirstLedger guards are there to prevent a uint32 underflow if a bogus value reaches ApplyFlags before ValidateFlags runs; ValidateFlags is what finally rejects it.
| // value of CHUNKS_PER_TXHASH_INDEX is seeded. Exported so the real | ||
| // metastore facade (#689) uses the exact same literal — the whole point | ||
| // of the immutability check is defeated if two sites disagree. | ||
| const ChunksPerTxHashIndexMetaKey = "config:chunks_per_txhash_index" |
There was a problem hiding this comment.
This key is exported on purpose. Once #689 lands the RocksDB-backed metastore facade, that code will read/write the exact same key. If two sites ever disagree on the literal string, the whole immutability check is defeated — a second run would see an 'absent' key and happily seed a new value next to the old one. Keeping the literal in exactly one place is the point.
| // We enforce that explicitly here instead of trusting the caller — a | ||
| // stray first-run invocation without Validate would otherwise seed "0" | ||
| // into the store and permanently lock the layout to that value. | ||
| func (c *Config) ValidateAgainstStore(store Store) error { |
There was a problem hiding this comment.
Three branches to verify below:
- Absent key → seed the current value and pass (first run).
- Stored == current → pass (subsequent run, layout unchanged).
- Stored != current → hard abort, with both values named in the error.
The precondition check at the top protects against a caller who forgot to run Validate() first. Without it we would silently seed "0" into the store on an un-initialized Config and permanently lock the on-disk layout to that bogus value.
| // check runs against. The interface is deliberately narrow (Get + Put | ||
| // only). This slice ships an in-memory implementation; a real | ||
| // RocksDB-backed one will land as part of #689. | ||
| type Store interface { |
There was a problem hiding this comment.
Both Store (Get + Put) and BSBAvailabilityProbe (MaxAvailableLedger) are deliberately narrow. This slice ships in-memory / no-op implementations so the subcommand can run the full pipeline end-to-end today. When the real RocksDB-backed store lands in #689 and the real GCS-backed probe in #688, those implementations drop in at the two cmd.go call sites — nothing else in this package has to change.
| // validate flags → immutability check → BSB availability → summary. | ||
| // Each step wraps its error so operators see the specific rule that | ||
| // failed. | ||
| func runBackfill(cmd *cobra.Command, _ []string) error { |
There was a problem hiding this comment.
Pipeline order is load-bearing, not cosmetic:
Validatefills defaults (includingCHUNKS_PER_TXHASH_INDEX=1000when absent).ApplyFlagsdepends on those defaults —BuildGeometryreadsChunksPerTxHashIndexto widen the range.ValidateFlagssanity-checks the raw CLI numerics.ValidateAgainstStorelocks the layout-definingCHUNKS_PER_TXHASH_INDEXvalue — requiresValidateto have run (enforced as a precondition in validate.go).ValidateAgainstBSBchecks the widened end ledger against BSB — requiresApplyFlagsto have run.
Re-ordering any step breaks a downstream consumer's precondition.
Keep require only where a failure would genuinely invalidate the subsequent check (Execute before reading out.String(), error non-nil before reading err.Error()). All independent equality / containment checks switched to assert so a single failing run surfaces every problem, not just the first.
|
Closing this PR (and its parent issue #684) as part of the rewrite of PRD #678. WhyTOML config parsing is no longer a backfill concern. Under the new framing in PRD #678, the unified
What survives, and where it landsThe Closing without merge. |
Important
Base is temporarily
karthik/backfill-impl/scaffold-subcommand(head of #699), notfeature/full-history. This branch stacks on #699; I'll re-target the base after #699 merges.Closes #684.
Summary
pkg/geometryfrom reference commit19a9179— Index-only API; drops the oldRange*nomenclature per the current design doc.backfill/config/with the UPPER_SNAKE_CASE TOML schema, plusParseConfig+MarshalTOMLround-trip.Validate— required fields + path defaults underDEFAULT_DATA_DIR.ApplyFlags— widens--start-ledger/--end-ledgeroutward to chunk boundaries; resolves the0 = GOMAXPROCSsentinel on--workers; merges--log-level/--log-formatoverrides.ValidateFlags+ValidateAgainstStore(CHUNKS_PER_TXHASH_INDEXimmutability) +ValidateAgainstBSB(availability probe).StoreandBSBAvailabilityProbeinterfaces with in-memory / no-op implementations. Real implementations replace these constructor calls as part of #689 and #688.--log-level/--log-formatflags.Unit tests
pkg/geometryChunkFirstLedger,ChunkLastLedger,IndexFirstChunk,IndexLastChunk, etc.).config/config_test.go[BACKFILL.BSB]).DEFAULT_DATA_DIR.MarshalTOMLround-trip.config/flags_test.goworkers=0→GOMAXPROCS;max-retriespasses through literally (including 0).BuildGeometrythreadsCHUNKS_PER_TXHASH_INDEX.config/validate_test.goDEFAULT_DATA_DIR, missing[BACKFILL.BSB], emptyBUCKET_PATH).ValidateFlags— start < 2, end ≤ start, workers < 0, max-retries < 0; workers=0 sentinel passes.ValidateAgainstStore— absent / match / mismatch branches via mockStore; precondition error whenValidate()was skipped.ValidateAgainstBSB— available / insufficient / probe-error branches via mock probe.backfill/cmd_test.gocmd.Execute(start < 2, end ≤ start, missingDEFAULT_DATA_DIR, missing[BACKFILL.BSB]).--configfile.