Skip to content

meta: unified metadata layer (json + sqlite engines) replacing storage.Store[T] #146

Description

@CMGS

Canonical design: cocoon-specs/design/meta-store.md — frozen at v2.28. This body is a summary; where they differ, the doc wins. The original inline design (v1) and the addendum comments below are superseded by the doc.

Motivation (measured)

Clone storms are dominated by the metadata layer, not virtualization: at B=64 with resident N≈190, the segments containing the two VM-index writes carry p50 2347ms + 867ms of a 4069ms clone while the FC snapshot load is 14ms. Every storage.Store[T] update rewrites the whole index plus a .prev copy under an exclusive flock (~1.8KB/record, 339KB×3 IO at N=192) ⇒ storm cost O(N·B). Removing a single RMW (#132/#145) measured inside host noise — the O(N) payload per write is the problem, and Store[T]'s whole-value semantics pin that cost model. Full data: cocoonstack/sandbox#30.

Decision summary

  • One record-granularity API, several engines (meta package): Scope-declared transactions with a single write namespace, per-transaction CommitMode (durable default, relaxed by explicit per-op opt-in), pure retryable closures, detached values, engine-neutral errors, Collection/Log primitives with domain repositories on top.
  • json is the first-class default engine — today's files, formats, .prev recovery and flocks move inside it; existing deployments upgrade untouched, no conversion. sqlite is the opt-in scale engine (one DB, namespace = table group, WAL, modernc.org/sqlite).
  • One meta.Store constructed at the command layer and injected into every backend — replacing five per-subsystem private store constructions.
  • GC and every metadata-backed destructive flow move to a revalidating tombstone protocol (lease fencing, phase-directed recovery, stable VMID-keyed entity locks outside cleanup sets), replacing lock-all orchestration. Bridge lane stays out (no persistent records).
  • Engine conversion is an explicit offline ops action (meta init / meta convert) with an engine-neutral manifest; config remains the sole backend authority.
  • Statistical acceptance gates per engine: sqlite gets slope + absolute ceilings + storm anchors; json gets paired-ratio non-regression (its O(records) cost is contractual).

Phasing

  • P0 — boundary only, json engine, semantics unchanged (byte-identical fixtures + differential trace); storage.Store[T] retires.
  • P1 — protocol changes, still json-only: tombstone protocol, stable VM lock, vm rm restructure, retirement of gc.Module.Locker / Watchable.WatchPath / IndexFile/IndexLock.
  • P2 — the sqlite engine and everything that exists only for it; the measured win lands here.
  • P3 — optional: metering Log, networked engines, widening the relaxed set with data.

Status

Design frozen at v2.28 (v2.26-v2.28 were implementation-round amendments recorded in the doc header; v2.28 records the P2/P3 as-built decisions: generic (id, data) physical schema superseding the domain-typed sketch, flag-less meta commands with meta_backend as sole authority, referential name check, composition-root manifest guard for the json engine, and the SPI-layered Log/Seq with its opt-in metalog consumer). The doc moved to cocoon-specs — cocoon no longer carries design documents.

  • P0 — complete (PR meta: unified metadata layer P0+P1 — record-granularity store, json engine, tombstone protocol #147): meta API + json engine + contract suite; all four subsystems on one command-layer meta.Store; storage.Store[T] retired; byte-fidelity goldens + differential value/error traces for every namespace.
  • P1 — complete (PR meta: unified metadata layer P0+P1 — record-granularity store, json engine, tombstone protocol #147): tombstone phase protocol across vm rm / snapshot delete / CNI teardown / GC; stable VMID-keyed locks; entrypoint discipline; per-digest blob locks on every re-pin flow; Store.Events; protocol gates (fencing/ABA, subset recovery, shared-lease escalation, 256-process storm, durable-ack ordering) all green.
  • P2 — complete (PR meta: sqlite engine, offline conversion, and the metering log (P2+P3) #148): sqlite engine (WAL, durable/relaxed writer pair, ctx-bounded busy retry surfacing ErrBusy, identity/meta_state fail-closed verification, prepared-statement cache, background passive checkpoint); offline conversion with the §6 manifest protocol (both directions, crash-rerunnable, quiesce probes, aside retirement); cocoon meta init|convert|backup; contract suite passes both engines unmodified. Measured on the 16-core NVMe anchor host: durable update 366µs at N=10k vs json 31.9ms (87x), get 8.8µs vs 24.5ms, concurrent creates 909-1,693/s vs json 94-129/s at resident 1,000 — the §9 absolute gates hold with 13-57x headroom.
  • P3 — Log/Seq re-added (PR meta: sqlite engine, offline conversion, and the metering log (P2+P3) #148): SPI-layered primitive (identical semantics on both engines, zero engine code) with the metering.backend: meta consumer, Relaxed durability matching the file backend. Networked engines and relaxed-set widening stay data-gated per the doc.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions