Expand description
The normalized subscription protocol (NORMALIZED-CHANGES-DESIGN.md §3) — the
path-free twin of the engine’s flat_protocol. The envelope semantics are identical
(a once-per-subscription NormalizedHello, a logical-seq-0 snapshot, then
gap-free incremental NormalizedBatches at seq 1, 2, …); only the payload
differs — table-tagged NormalizedOps instead of path-tagged FlatChanges — and
the hello is slimmer: flat per-table schemas (for positional-row alignment + a
normalized_fp), not a nested hierarchical view schema (§3).
Why a sibling here rather than a generic reuse of flat_protocol: that module’s
Publisher/Subscriber/Batch are concrete over FlatChange + a hierarchical view
Schema, and flat_protocol lives in the wasm-clean rindle core; normalize lives
beside this module in rindle-wire (also wasm-clean, but the protocol crate, not
the engine — see the placement note in crate::normalize). So this re-implements
the small epoch/seq/gap machinery over the normalized payload rather than
generic-ifying core. The contract it enforces is the flat one:
- Sender (
NormalizedPublisher) wraps aNormalizeFold, drives the seq counter, and stamps every frame with the subscriptionepoch+normalized_fp. An empty transaction emits no batch and consumes no seq (so seq stays gap-free over emitted batches). - Receiver (
NormalizedSubscriber, the validation half — the fold itself is the TSNormalizedSync, Slice 4) enforces the comparator contract at the hello and, per batch, epoch + fingerprint + strict in-order seq. A duplicate seq is discarded idempotently (rcadd/remove are not idempotent). A gap is fatal: the only repair is a full re-hydrate under a new epoch (§5.3), after which stale old-epoch frames are rejected byNormalizedProtocolError::EpochMismatch.
Snapshot chunking (the flat_protocol SnapshotChunk path) is deferred: this
slice ships the single-shot seq-0 snapshot. Chunking is mechanical to add later (the
design §3 notes it as a capability, not a v1 requirement).
Structs§
- Normalized
Batch - One committed transaction’s normalized ops (or the seq-0 hydrate snapshot). Ops apply
in order into one client
Db.write()transaction. - Normalized
Hello - The subscription handshake (§3), sent once before any
NormalizedBatch. Slimmer than the flatHello: flat per-table schemas, no nested view schema or per-level sort. - Normalized
Publisher - Sender side: wraps a
NormalizeFold, stamps batches with the subscriptionepoch+normalized_fp, and drives the gap-free seq. The caller drains the change-sink (the replica’s per-queryCaughtChanges) and hands them tosnapshot/commit. - Normalized
Schema Error - A malformed table set advertised by a
NormalizedHello.table_indexidentifies the offending entry inNormalizedHello::tables; the table name itself stays in the hello so this error remains small andCopywhile callers can still render a precise diagnostic. - Normalized
Subscriber - Receiver side: the protocol state machine that validates the envelope and emits
clean ops for the caller (the base-store fold / TS
NormalizedSync) to apply. It does not itself hold the base store — it owns only epoch/seq/fp state. - Progress
Frame - The connection-level progress frame (§8.6): advances the client’s coherent release
point (
cv_min). Pure release signal — mutation confirmation does NOT ride it:lmidis a row in the client-mutations table (_rindle_client_mutations,rindle-replica’sCLIENT_MUTATIONS_TABLE), delivered through the client’s own per-client system query like any other data, so it is released by the samecv_minthat releases the commit’s effects (transactionally coherent by construction). Emitted per the poke rule (§8.4); standalone only to advancecv_minduring a quiet window. - Table
Wire Schema - One base table’s flat schema on the wire: its ordered column names (wire rows are
positional against them) and the primary-key column indices. The client validates
these against its own typed schema and registers any table it hasn’t yet (e.g. an
EXISTS witness table). No
sort— base tables sort by PK in the memory source, and the result sort is the client’s local engine’s concern (§3).
Enums§
- Normalized
Applied - The outcome of applying a
NormalizedBatch. - Normalized
Protocol Error - A protocol violation the
NormalizedSubscribersurfaces. All but a duplicate are fatal — the only repair is a re-hydrate under a new epoch (§5.3). - Normalized
Schema Error Kind - The semantic wire-schema rules required by
normalized_fpand by consumers that register the advertised tables as keyed sources.
Functions§
- build_
query_ parts - The query-wide pieces a normalized subscription needs, derived once from the AST + the
surfaced-table schemas: the
NormalizeFold(the shared footprint serializer over the pipeline’s full rows), the projected wire table set and itsnormalized_fp, and theProjMapthat drives project-at-emit. Splitting this out lets a shared engine query own ONE fold + projection while each subscriber builds its ownNormalizedHellofrom the same projected table set (hello_from_parts) — fold, projection, and hello agree by construction. - collect_
tables - Every table
node’s subtree can surface (pub:rindle-room-core’s downstream materialization walks the same set to assemble its schemas). - hello_
from_ parts - The per-subscriber handshake for a (possibly shared) normalized query at
epoch, from the query’s deterministic table set +normalized_fp(seebuild_query_parts). - normalized_
fp - Fingerprint a
tablesset by name (resolving PK to column names), independent of internal column numbering.tablesmust satisfyvalidate_normalized_schema; the publisher constructors guarantee that invariant, andNormalizedSubscriber::openvalidates received schemas before calling this function. - project_
ops - Project every op’s rows to the per-table column set (§5.2). A no-op when
projis empty.pub(notpub(crate)):rindle-replica’sDrainapplies the same projection on its own fanout path, and the room’s downstream publisher will too. - unsafe_
int_ in_ normalized_ batch - The first
Intin this batch’s rows outsideNumber.MAX_SAFE_INTEGER, if any — the 09.8strict_i64walk for the normalized wire (design 226 Stage A). A JS-facing boundary with strict mode on refuses the batch with a typed error instead of letting the cell encode round it (wire_json’sInt → f64collapse). - validate_
normalized_ schema - Validate the table-set invariants required by
normalized_fpand normalized-store construction. This accepts wire-deserialized input, so every failure is typed and no malformed schema may panic.
Type Aliases§
- ProjMap
- Project-at-emit map (
PROJECTION-SUPPORT-DESIGN.md§5.2): table name → the base column indices this query syncs for it, in ascending (= projected wire) order. Only projected tables appear; a table absent here syncs full. An empty map ⇒ no projection, so emission is a pass-through (a'*'query is byte-identical, §7).