You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of #808 (epic) · Builds on: feature/full-history · Reference implementation: #805
Slice 1 · Layer 1 of 4 — Foundations. The durable-state substrate of the streaming daemon, with no daemon goroutines yet — self-contained and reviewable on its own.
Context
The daemon keeps two tiers — hot (recent ledgers in a per-chunk RocksDB) and cold (older ledgers as immutable files). All durable state is tracked in a small catalog (meta-store RocksDB) via a strict one-write protocol, so cleanup walks catalog keys and never lists directories. This layer builds that substrate — the types, the catalog API, config, and locking that every later layer composes. It produces no running daemon.
single-process flock on the catalog path and every configured storage root + the hot-storage root (a second daemon on any shared root exits)
Cross-cutting primitives
RetentionGate — the reader-retention predicate (below-floor ⟹ not-found; ChunkBelowFloor)
the ArtifactSet / Kind abstraction + crash hooks
Acceptance
Crash-safety: simulated power-loss between each ordered protocol step; "every file has its key" and "key absent ⟹ file gone" hold at every interruption; multi-key batch atomicity.
Config: the loader parses valid configs and applies defaults. The malformed-case matrix (zero/over-max cpi, zero workers, negative retries, misaligned/sub-genesis floor) is rejected by validateConfig, which lives in Layer 4 (Streaming daemon: Slice 1 · Layer 4 — Daemon assembly, operability & validation #812 — it calls networkTip); those rejection assertions ship there, not in this layer's standalone tests.
Locking: two daemons sharing any root are blocked.
Green standalone:go build + go vet + go test -short pass for the package on its own (no later layers).
validateConfig in full — both the structural malformed-case rejections and the network-dependent earliest-ledger resolution → Layer 4 (Streaming daemon: Slice 1 · Layer 4 — Daemon assembly, operability & validation #812); it calls networkTip (a startup concern), so the whole function lands there — L1 supplies only the schema / loader / defaults
any daemon goroutine, hot-DB ingestion, freezing, the resolver → Layers 2–4
the events and tx-hash data types → Slices 2 and 3
Part of #808 (epic) · Builds on:
feature/full-history· Reference implementation: #805Slice 1 · Layer 1 of 4 — Foundations. The durable-state substrate of the streaming daemon, with no daemon goroutines yet — self-contained and reviewable on its own.
Context
The daemon keeps two tiers — hot (recent ledgers in a per-chunk RocksDB) and cold (older ledgers as immutable files). All durable state is tracked in a small catalog (meta-store RocksDB) via a strict one-write protocol, so cleanup walks catalog keys and never lists directories. This layer builds that substrate — the types, the catalog API, config, and locking that every later layer composes. It produces no running daemon.
Scope
pkg/chunk)chunkFirstLedger/chunkLastLedger;LedgersPerChunk = 10_000; genesis = ledger 2chunk / 1000,%05d) for the on-disk directory layoutchunk −1(chunkLastLedger(-1) = 1) for the "nothing ingested yet" watermarkchunk:{c:08d}:{ledgers|events|txhash},hot:chunk:{c:08d},config:*pinsledgerskind; theevents/txhashkinds arrive in Slices 2–3freezing | frozen | pruning(per-chunk artifacts) andtransient | ready(hot DB)State(chunk, kind)/HotState(chunk)— artifact & hot-DB stateChunkArtifactKeys(),HotChunkKeys(),ReadyHotChunkKeys()— enumerate keysEarliestLedger()— config-pin read; plus low-levelGet/HasFrozenCoverage/IndexKeysreads are added in Slice 3)"freezing"before any I/O"frozen"(single put per-chunk; atomic commit batch for multi-key)"pruning"→ unlink →fsyncDir→ delete key ⟹ key absent ⟹ file gonesweepChunkArtifacts)fsyncDir→ delete key, batched per family[service],[backfill],[backfill.bsb],[immutable_storage.*],[catalog],[streaming],[streaming.hot_storage],[logging]) + loader + defaultsvalidateConfigfunction that rejects malformed values lives in Layer 4 (Streaming daemon: Slice 1 · Layer 4 — Daemon assembly, operability & validation #812), because it also callsnetworkTip— see Acceptance and Out of scopeflockon the catalog path and every configured storage root + the hot-storage root (a second daemon on any shared root exits)RetentionGate— the reader-retention predicate (below-floor ⟹ not-found;ChunkBelowFloor)ArtifactSet/Kindabstraction + crash hooksAcceptance
validateConfig, which lives in Layer 4 (Streaming daemon: Slice 1 · Layer 4 — Daemon assembly, operability & validation #812 — it callsnetworkTip); those rejection assertions ship there, not in this layer's standalone tests.go build+go vet+go test -shortpass for the package on its own (no later layers).Design references
Dependencies
feature/full-history)pkg/chunk,pkg/rocksdb,pkg/stores/metastoreOut of scope (later layers)
validateConfigin full — both the structural malformed-case rejections and the network-dependent earliest-ledger resolution → Layer 4 (Streaming daemon: Slice 1 · Layer 4 — Daemon assembly, operability & validation #812); it callsnetworkTip(a startup concern), so the whole function lands there — L1 supplies only the schema / loader / defaultseventsandtx-hashdata types → Slices 2 and 3