fullhistory: reorganize packages (#824)#844
Conversation
Pure reorganization — moves + mechanical renames, zero behavior change. - pkg/ -> storage/ (rename the meaningless pkg/ to the storage tier's name; chunk, rocksdb, and per-domain stores stay grouped under it) - extract config/ (config.go + config_lock.go); config_validate.go stays in root as startup orchestration (depends on catalog + NetworkTipBackend) - extract durable/ leaf for the os-level fsync/barrier helpers, so geometry is naming + math only (per tamirms PR #818 review) - drop the errLockHeld relay sentinel; acquireLock wraps ErrRootLocked directly - sweep the streaming codename from operator-facing messages and stale "streaming package/daemon" comments Deferred to a follow-up: BeginHotCreate/FinishHotCreate choreography, the hotchunk open-ready helper, and test-fixture consolidation (behavioral refactors kept out to keep this a reviewable pure-reorg).
…#824) The behavioral cleanups agreed on #824, kept out of the pure-reorg commit. - catalog: BeginHotCreate (wipe + transient) / FinishHotCreate (fsync + flip ready) move the hot-DB create choreography behind the catalog, mirroring MarkChunkFreezing/FlipChunkFrozen; hotloop.openHotDBForChunk sandwiches hotchunk.Open between the pair and no longer does raw os/fsync itself. - hotchunk.OpenReady is the single enforcement site for "ready key ⇒ must-exist, never-creating open": openHotDBForChunk, refineWithHotDB, and tryHotSource all route through it. hotchunk gains a geometry import (HotState); acyclic. - catalog.DeleteHotKey unexported to deleteHotKey (only prod caller is same-package DiscardHotChunk); its raw-key round-trip test moves into the catalog package. PutHotTransient/FlipHotReady stay exported — genuine cross-package test-setup primitives. - New fhtest package holds the shared ZeroTxLCMBytes fixture, replacing three byte-identical copies. Not done: the review also named testSink/execRecorder for consolidation, but each exists in exactly one package (not duplicated), so merging them would only force package-internal interfaces to be exported. Left as-is.
| // live is the highest "ready" hot chunk (from ReadyHotChunkKeys), so route the | ||
| // read-only open through the single OpenReady enforcement site: must-exist, | ||
| // never auto-healed, uniform won't-open error. | ||
| hot, openErr := hotchunk.OpenReady(geometry.HotReady, cat.Layout().HotChunkPath(id), id, logger, true) |
There was a problem hiding this comment.
The PR description resolves "progress + retention: stay in lifecycle — freeze/prune policy," but that rationale only fits retention (the tick's gate). Nothing in the lifecycle package has called anything in progress.go since the clamp deletion — its only production caller is startup.go — and #835's recorded scope (from your deferral on #820) includes moving it next to that caller in the fullhistory root. Since this PR is exactly the place for pure moves, suggest doing it here: git mv progress.go to the root, unexport LastCommittedLedger (same package as its caller now), and #835 shrinks to just the logger change. If you'd rather not, no problem — but then the resolution text should say "deferred to #835," not "stays."
There was a problem hiding this comment.
Done in 8fa6c1a: git mv to the root next to startup.go, LastCommittedLedger → unexported lastCommittedLedger, and its three test files moved along (the root test helpers already covered the scaffolding, including openLiveHotDB). The lifecycle tests that used deriveCompleteThrough as a fixture oracle now pass their fixture-known values directly — runTickForCatalog collapsed into direct runLifecycle(…, lastChunk) calls, so tick inputs are explicit rather than re-derived by the code under test's own family. PR description updated; noted on #835 that its scope shrinks to the logger-parameter change.
| return nil, fmt.Errorf("%w: %q (another daemon is using it)", ErrRootLocked, root) | ||
| } | ||
| return nil, fmt.Errorf("streaming: lock %q: %w", path, err) | ||
| return nil, fmt.Errorf("fullhistory: lock %q: %w", path, err) |
There was a problem hiding this comment.
The sweep renamed streaming: → fullhistory: on the error strings, but the plan recorded on #841 is to delete these prefixes outright: an error's location comes from its wrap chain (callers already wrap with "read config", "lock root", etc.), so a self-locating prefix is redundant — renaming it just moves the problem from a codename to a package name. Since you're already sweeping every one of these strings in this PR, deleting the prefix instead is the same mechanical pass and closes #841's bullet rather than half-addressing it. If you prefer keeping the prefix, that's shippable too — just needs a note on #841 so its bullet shrinks to the four Info/Debug log lines.
There was a problem hiding this comment.
Done in 8fa6c1a — deleted outright rather than renamed: all 19 remaining error strings (config ×2, config_lock ×6, catalog ×6, geometry ×5; the 7th config_lock one was errLockHeld, already dropped in this PR), and the four Info/Debug log lines followed the same decision since it's the same mechanical pass. Noted on #841 that its prefix bullet is closed here.
| // txhash, metastore) and the cross-cutting error sentinels they | ||
| // emit. Per-domain stores translate their backing primitive's | ||
| // errors (pkg/rocksdb, pkg/packfile, os) into these sentinels at | ||
| // errors (storage/rocksdb, storage/rocksdb, os) into these sentinels at |
There was a problem hiding this comment.
The sweep garbled this line: "(storage/rocksdb, storage/rocksdb, os)" — the second one was pkg/packfile in the base (itself stale; packfile lives at internal/packfile). Should read "(storage/rocksdb, internal/packfile, os)".
There was a problem hiding this comment.
Fixed in 8fa6c1a — now reads "(storage/rocksdb, internal/packfile, os)".
There was a problem hiding this comment.
One codename straggler the sweep missed, at rocksdb.go:579: "The streaming ingestion contract requires…" — that's the retired codename, not the domain word. "The ingestion contract" says it.
There was a problem hiding this comment.
Fixed in 8fa6c1a — "The ingestion contract requires…".
| // a missing or gutted "ready" DB fails the open — never auto-healed into a fresh | ||
| // empty one — wrapped in the uniform won't-open error so every ready-open site | ||
| // reports it identically. | ||
| func OpenReady( |
There was a problem hiding this comment.
Optional: the trailing readOnly bool makes the call sites read OpenReady(state, dir, id, logger, false) — a naked boolean the reader has to look up. Two thin names over an unexported core — OpenReadyWrite / OpenReadyView (or similar) — would keep the single enforcement site while letting each call site say what it means.
There was a problem hiding this comment.
Done in 8fa6c1a: OpenReadyWrite / OpenReadyView over an unexported openReady core — the enforcement site stays single, and the three call sites (hotloop resume handle, freeze source, last-committed refiner) now say what they mean without the naked boolean.
|
Two more code changes belong in this PR, and they compound:
With those two, this PR finishes #824 and #835 outright. I'd stop there: the #841 deletions (~300 lines of dead API plus test migrations) are a different kind of change and should be their own PR. |
…enReadyWrite/View (#824) - Move progress.go (+ its three test files) from lifecycle/ to the fullhistory root next to its only production caller, startup.go; unexport LastCommittedLedger -> lastCommittedLedger. Lifecycle tests that re-derived 'through' as an oracle now pass their fixture-known values directly (runTickForCatalog collapsed into runLifecycle calls). - Delete the 'fullhistory: ' self-locating prefixes outright (19 error strings + 4 Info/Debug log lines) instead of keeping the rename; error context comes from the wrap chain (#841). - Split hotchunk.OpenReady into OpenReadyWrite/OpenReadyView over an unexported openReady core, so call sites name the handle they take. - Fix the garbled sentinel-translation comment in stores/errors.go and the retired 'streaming' codename straggler in rocksdb.go.
…t) (#824, #835) - metastore was a 115-line rocksdb wrapper with exactly one consumer. Its string-KV surface is now catalog-internal (catalog/kv.go, unexported); NewCatalog(store, ...) becomes catalog.Open(path, layout, txLayout, logger) — the catalog owns its backing store, Close releases it. Gone with the fold: the two-step daemon wiring, the metastore.BatchWriter type threaded through the protocol/sweep closures, the ErrNotFound manufacture-then-translate round-trip in Catalog.get, and metastore.New's guards duplicating rocksdb.New's. The KV test suite moves to catalog/kv_test.go. - The catalog now carries a logger, so refineWithHotDB uses cat.Logger(), the logger parameter drops, and lastCommittedLedger(cat) lands as the design wrote it (#835). The deriveCompleteThrough/ deriveLastCommitted test-shim aliases collapse into direct calls. - backfill's skip-when-coverage-frozen test seeded raw "pruning" keys on the bare store; with no bare store to reach it now runs the real SweepChunkArtifacts — the finalized-window scenario it simulates.
|
Both done in a051a06 (and the progress.go move from the inline thread landed in 8fa6c1a):
One test seam changed shape: backfill's skip-when-coverage-frozen test used to seed raw "pruning" chunk keys on the bare store; with no bare store to reach, it now runs the real Agreed on stopping there — the only #841 piece taken here is the error-prefix bullet you flagged inline; the ~300-line dead-API deletions stay a separate PR. PR description updated ( |
CI floats on the stable channel (rust-toolchain.toml + rustup update in every workflow) and picked up rustc 1.97.0, under which ethnum 1.5.2 fails to compile: its error.rs does mem::transmute(()) into core::num::TryFromIntError, whose size changed (E0512). ethnum 1.5.3 constructs the error values from real from_str_radix failures instead. Every job dies in the shared make build-libs step, so this cleared the whole matrix; main needs the same bump separately.
Closes #824. Closes #835. The
streaming/catch-all was already split intogeometry/catalog/ingest/backfill/lifecycle+ per-domain stores in the #817→#820 stack; this PR does the leftover moves plus the safe cleanups agreed on the issue.This is a pure reorganization — moves + mechanical renames, zero behavior change. No production logic is altered.
What changed
1.
pkg/→storage/pkg/is meaningless inside an already-internal/tree. Rather than a flat drop (the issue's literal ask), this renames it tostorage/— a meaningful name for the storage tier that sits below the daemon logic, keepingchunk,rocksdb, and the per-domainstores/grouped.2. Extract
config/config.go+config_lock.go(schema, load/parse,Paths→Layout, root locks) move into aconfig/package. Cycle-safe: onlymain.goimports the root package and it usesRunDaemon(ctx, path)— no config symbols.config_validate.gostays in root:validateConfigdepends oncatalog.Catalogand the root-definedNetworkTipBackend, so it's startup orchestration, not static config — moving it would create aconfig → rootcycle.3. Extract
durable/(fsync helpers)Per tamirms' review: the 7 os-level durability ops (
BarrierNewFile,FsyncDir,FsyncParentDirs,FsyncNewDirs,DeepestExistingDir,DeleteFileIfExists,RmdirIfEmpty) had zero geometry/domain references — they only sat ingeometry/paths.goby accident of sharing the file with the path bijection. They move to adurable/leaf imported byconfig,catalog,backfill, and the daemon.geometryis now naming + math only (noosimport).4. Safe ride-along cleanups
errLockHeldrelay sentinel —acquireLockwrapsErrRootLockeddirectly (one producer, one consumer). issue commentstreamingcodename from operator-facing error/log messages: the self-locating prefixes are deleted outright (per the full-history: dead pre-wired surface + stale contract docs (~300 lines deletable) #841 plan — an error's location comes from its wrap chain, so a prefix is redundant whether it saysstreaming:orfullhistory:); the four Info/Debug log lines follow the same decision. Comments naming a now-nonexistent "streaming package/daemon" reworded. Left domain uses of "streaming", thefullhistory_streamingPrometheus subsystem, and metastore test-key literals (data/interface formats, not codenames).Open-question resolutions (from #824)
chunkis imported by the stores;geometryimports the stores → a merge would cycle).daemon.goorchestration.lifecycle— since the soroban-rpc: Replace all hex-encoded contract ids with strkeys #25 clamp deletion, progress.go's only production caller isstartup.go, so it moves next to it (unexportedlastCommittedLedger). Retention remainslifecycle's freeze/prune policy gate.5. Behavioral cleanups (second commit)
The logic refactors from the review, kept in a separate commit (
fullhistory: hot-create choreography, OpenReady, test-fixture cleanup) so the reorg commit above stays a reviewable pure-reorg:cat.BeginHotCreate/cat.FinishHotCreate— the hot-DB create choreography (wipe + transient key →hotchunk.Open→ fsync chain → flip ready) moves behind the catalog, mirroringMarkChunkFreezing/FlipChunkFrozen.hotloop.openHotDBForChunknow sandwicheshotchunk.Openbetween the pair and no longer does rawos/fsync itself. discussion_r3517588234hotchunk.OpenReadyWrite/OpenReadyView— a thin named pair over one unexportedopenReadyenforcement site for "ready key ⇒ must-exist, never-creating open," so call sites name the handle instead of passing a naked boolean.openHotDBForChunk(write),refineWithHotDB, andtryHotSource(view) all route through it.hotchunkgains ageometryimport forHotState(verified acyclic). discussion_r3517588278DeleteHotKey→deleteHotKeyunexported (only prod caller is same-packageDiscardHotChunk); its raw-key round-trip test moves into thecatalogpackage.PutHotTransient/FlipHotReadystay exported — they're genuine cross-package test-setup primitives.fhtestpackage holds theZeroTxLCMBytesfixture, replacing three byte-identical copies.6. Fold
metastoreintocatalog(finishes #824) +lastCommittedLedger(cat)(finishes #835)Per review comment:
metastorewas a 115-line rocksdb wrapper with exactly one consumer. The string-KV surface is now catalog-internal (catalog/kv.go, unexported), andNewCatalog(store, …)becomescatalog.Open(path, layout, txLayout, logger)— the catalog owns its backing store,Closereleases it. Gone with the fold: the two-step daemon wiring, themetastore.BatchWritertype threaded through the protocol/sweep closures, theErrNotFoundmanufacture-then-translate round-trip inCatalog.get, andmetastore.New's guards that duplicatedrocksdb.New's. The KV test suite moved tocatalog/kv_test.go.The fold hands over the rest of #835: the catalog now carries a logger, so
refineWithHotDBusescat.Logger(), theloggerparameter drops, andlastCommittedLedger(cat)lands as the design wrote it. ThederiveCompleteThrough/deriveLastCommittedtest-shim aliases collapsed into direct calls (the nil-logger seam #835 worried about was already gone — those tests run against real hot DBs).One test seam changed shape: backfill's skip-when-coverage-frozen test used to seed raw
"pruning"chunk keys on the bare store; with no bare store to reach, it now runs the realSweepChunkArtifacts— which is exactly the finalized-window scenario it was simulating.Verification
go build,go vet,go test -short ./cmd/stellar-rpc/internal/fullhistory/..., and the repo golangci-lint all green (rocksdb cgo) at every commit. Renames are git-mv (history preserved); import rewrites are mechanical (gofmt -r, AST-based).