Documentation
¶
Overview ¶
Package sqlite is Harbor's SQLite-backed `memory.MemoryStore` driver. It is the second leg of the memory persistence triad (in-memory floor, SQLite, Postgres) defined by RFC §6.6 + §9.
The driver is built on `modernc.org/sqlite` — a CGo-free SQLite engine (D-013, AGENTS.md §5). Builds remain `CGO_ENABLED=0`.
Strategy delegation (Phase 25a, D-174) ¶
All three memory strategies (`none`, `truncation`, `rolling_summary`) are implemented by the driver-agnostic `internal/memory/strategy` executor package; this driver is a thin shell that owns the boundary (identity validation + the `memory.identity_rejected` emit + the `closed` flag) and delegates every `MemoryStore` method to a `strategy.StrategyExecutor`. The executor persists state through the injected `state.StateStore` (D-027 typed wrapper, `Kind = "memory.state"`). When that StateStore is itself SQLite-backed (the operator's `state.driver: sqlite`), the memory strategies persist durably to disk — which is what makes `truncation` / `rolling_summary` survive a restart. No strategy algorithm is reimplemented in SQL here; the SQLite + Postgres memory drivers gain all three strategies through the same executor the InMem driver uses.
The driver still opens its own `*sql.DB` against `cfg.DSN`. The connection + the embedded `memory_state` migration are retained so the driver fails loudly on a misconfigured DSN at boot (and so the schema exists for any out-of-band tooling), but the live read/write path rides entirely on the executor's `state.StateStore` writes. The driver's own `memory_state` table is vestigial under delegation; it is kept (never edited — migrations are forward-only, AGENTS.md §9 / §13) for back-compat with rows written by the pre-25a strategy=none path.
Operating model for the retained connection:
- Database opened against `cfg.DSN`. Bare file paths and the special `:memory:` sentinel are supported. URI-form DSNs (`file:foo.db?...`) pass through with `_pragma` + `_txlock` query params layered on top so per-connection PRAGMAs survive `database/sql`'s connection lifecycle.
- WAL journal mode is pinned at open.
- `busy_timeout=5000` (5 s) absorbs `SQLITE_BUSY` retries.
- `db.SetMaxOpenConns(1)` pins the pool to a single connection.
- The schema is applied via embedded `migrations/*.sql` files (forward-only, AGENTS.md §13). The runner is idempotent.
The driver self-registers under `"sqlite"` from its `init()`. The production binary picks it up via blank import in `cmd/harbor/main.go`; tests may call `New` directly to skip the registry.
Concurrency contract (D-025):
- The driver struct holds the strategy executor (internally synchronised per-key), a `*sql.DB` (an internally-synchronised pool), and an `atomic.Bool` close flag. All are safe for N concurrent goroutines without external locking.
- Per-call state lives on the call stack / supplied `ctx`. Nothing mutable on the driver ever crosses run boundaries.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func New ¶
func New(cfg memory.ConfigSnapshot, deps memory.Deps) (memory.MemoryStore, error)
New constructs a SQLite-backed `memory.MemoryStore` against `cfg.DSN`. Production callers go through `memory.Open`; tests may call `New` directly to skip the registry.
The configured strategy is resolved by the shared `strategy.StrategyExecutor` (Phase 25a, D-174): `none`, `truncation`, and `rolling_summary` all delegate to the executor, which persists through `deps.State`. `rolling_summary` requires a non-nil `deps.Summarizer`; the executor's `New` rejects a nil summariser for that strategy with a wrapped error — fail loudly, never a stub fallback (AGENTS.md §13).
DSN handling mirrors the SQLite StateStore + ArtifactStore drivers: bare file paths and the special `:memory:` sentinel are supported; the driver appends `_pragma=busy_timeout(5000)` + `_pragma=journal_mode(WAL)` + `_txlock=immediate` query params so every pooled connection sees the same per-connection PRAGMAs.
`deps.Bus` is required (for the fail-closed identity-rejection emit path). `deps.State` is required — it is the persistence floor the strategy executor writes through.
Types ¶
This section is empty.