Documentation
¶
Overview ¶
Package durable is Harbor's StateStore-backed durable event log driver (RFC §6.13).
Architecture:
- The driver implements events.EventBus + events.Replayer. It owns its own monotonic, gap-free sequence counter and its own subscriber fan-out (drop-oldest under saturation, bus.dropped notices windowed per DropWindow) — it is a standalone §4.4 driver, not a wrapper over the inmem driver.
- Every published event is persisted through a state.StateStore before it is fanned out to live subscribers. Persistence is keyed so replay-from-cursor is exact and gap-free across a Runtime restart: see the keying scheme below.
- Replay reads from the StateStore — not an in-memory ring — so a late subscriber that connects after the Runtime was rebuilt against the same StateStore sees the full, gap-free history.
- At construction (durable mode) the monotonic sequence counter is rehydrated from the persisted head records (recoverNextSeq, via the StateStore.ListKind maintenance scan) so a Runtime rebuilt against the same StateStore issues sequences strictly greater than any pre-restart token — a client reconnecting at a high Last-Event-ID is never silently skipped.
- When NO StateStore is configured the driver auto-degrades to a best-effort in-memory ring buffer AND emits a loud runtime.warning event plus an slog.Warn (CLAUDE.md §13 "no silent degradation"). Replay is then NOT durable across restarts.
Keying scheme (within state.StateStore's keyed-slot contract — there is no list/scan method, so the durable log is built from one mutable "head" record plus one immutable "entry" record per event):
- The durable log is SESSION-scoped, matching events.Cursor which is (SessionID, Sequence). Both record kinds are stored under the session triple with RunID="" — an event's own RunID is preserved INSIDE the persisted bytes, not in the storage key.
- Head record: Kind = "events.durable.head" — holds the ordered list of bus-sequences persisted for that session.
- Entry record: Kind = "events.durable.entry/<seq>" — holds the JSON-encoded event for bus-sequence <seq>.
On Publish: assign the next bus sequence, write the entry record, then read-modify-write the head record's sequence list — all under publishMu so the head list and the sequence counter never disagree. A torn write (entry persisted, head not yet advanced) never produces a GAP in a served replay: Replay only ever returns sequences the head record lists, and the next Publish re-derives the head list.
The driver is registered under name "durable" via init(); cmd/harbor blank-imports this package so the registration fires at process startup. Per CLAUDE.md §4.4.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func New ¶
func New(ctx context.Context, cfg config.EventsConfig, r audit.Redactor, store state.StateStore, opts ...Option) (events.EventBus, error)
New constructs the durable driver directly. Exposed for tests and for cmd/harbor's wiring path (which opens the StateStore and hands it in). When store is nil the driver runs in best-effort ring-buffer mode and emits a loud warning — see the package doc.
In durable mode New does construction-time I/O: it rehydrates the monotonic sequence counter from the persisted head records (see recoverNextSeq) so post-restart sequences stay strictly greater than any pre-restart token. ctx is the first parameter because of that I/O (CLAUDE.md §5) and bounds the recovery scan. A scan or decode failure fails the construction loudly — the driver never silently starts the counter at 0 (CLAUDE.md §13).
Types ¶
type Option ¶
type Option func(*bus)
Option configures the driver at construction. The exported options are test/operator seams; production callers that go through the registry use the defaults.
func WithClock ¶
WithClock injects a Clock. Tests use a controllable clock; the default realClock is correct for production.
func WithLogger ¶
WithLogger injects the slog.Logger the loud-degradation path writes to. Defaults to slog.Default(). Tests inject a capturing handler so the warning is assertable.