durable

package
v1.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 26, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

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

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 Clock

type Clock interface {
	Now() time.Time
}

Clock abstracts time so tests do not depend on the wall clock.

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

func WithClock(c Clock) Option

WithClock injects a Clock. Tests use a controllable clock; the default realClock is correct for production.

func WithLogger

func WithLogger(l *slog.Logger) Option

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL