Rindle docs and package mapSkip to main content

Module normalize_protocol

Module normalize_protocol 

Source
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 a NormalizeFold, drives the seq counter, and stamps every frame with the subscription epoch + 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 TS NormalizedSync, Slice 4) enforces the comparator contract at the hello and, per batch, epoch + fingerprint + strict in-order seq. A duplicate seq is discarded idempotently (rc add/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 by NormalizedProtocolError::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§

NormalizedBatch
One committed transaction’s normalized ops (or the seq-0 hydrate snapshot). Ops apply in order into one client Db.write() transaction.
NormalizedHello
The subscription handshake (§3), sent once before any NormalizedBatch. Slimmer than the flat Hello: flat per-table schemas, no nested view schema or per-level sort.
NormalizedPublisher
Sender side: wraps a NormalizeFold, stamps batches with the subscription epoch + normalized_fp, and drives the gap-free seq. The caller drains the change-sink (the replica’s per-query CaughtChanges) and hands them to snapshot / commit.
NormalizedSchemaError
A malformed table set advertised by a NormalizedHello. table_index identifies the offending entry in NormalizedHello::tables; the table name itself stays in the hello so this error remains small and Copy while callers can still render a precise diagnostic.
NormalizedSubscriber
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.
ProgressFrame
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: lmid is a row in the client-mutations table (_rindle_client_mutations, rindle-replica’s CLIENT_MUTATIONS_TABLE), delivered through the client’s own per-client system query like any other data, so it is released by the same cv_min that releases the commit’s effects (transactionally coherent by construction). Emitted per the poke rule (§8.4); standalone only to advance cv_min during a quiet window.
TableWireSchema
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§

NormalizedApplied
The outcome of applying a NormalizedBatch.
NormalizedProtocolError
A protocol violation the NormalizedSubscriber surfaces. All but a duplicate are fatal — the only repair is a re-hydrate under a new epoch (§5.3).
NormalizedSchemaErrorKind
The semantic wire-schema rules required by normalized_fp and 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 its normalized_fp, and the ProjMap that drives project-at-emit. Splitting this out lets a shared engine query own ONE fold + projection while each subscriber builds its own NormalizedHello from 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 (see build_query_parts).
normalized_fp
Fingerprint a tables set by name (resolving PK to column names), independent of internal column numbering. tables must satisfy validate_normalized_schema; the publisher constructors guarantee that invariant, and NormalizedSubscriber::open validates 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 proj is empty. pub (not pub(crate)): rindle-replica’s Drain applies the same projection on its own fanout path, and the room’s downstream publisher will too.
unsafe_int_in_normalized_batch
The first Int in this batch’s rows outside Number.MAX_SAFE_INTEGER, if any — the 09.8 strict_i64 walk 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’s Int → f64 collapse).
validate_normalized_schema
Validate the table-set invariants required by normalized_fp and 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).