Expand description
§Embedded SQLite with incremental live queries
Db provides one controlled SQL writer, ordinary SQL reads, and live queries
over file-backed SQLite. It captures row changes automatically and derives query
deltas through the rindle engine. Use it when an application owns its database
and needs live results without implementing change capture or transaction ordering.
Cluster distributes queries across worker threads. Its channel exposes
provisional deltas and commit progress; ClusterConsumer owns the drain loop
and adds normalized subscriptions. The raw rindle crate is a lower-level choice
for applications that already own source storage and change capture.
§Transactions and delivery
Every row write must pass through the controlled writer. The preupdate hook captures changes, and the engine derives against a read-only pre-commit snapshot plus an in-memory batch overlay. Derivation does not write SQL or replay triggers.
Db derives before commit, commits the durable data, then calls subscribers
synchronously. Cluster workers can emit Changed slices before commit; callers
must stage them until the hosting worker emits ClusterEvent::Progressed.
See Update and ClusterEvent for recovery and delivery boundaries.
Query handles expose changes rather than maintaining an application view. Apply
changes in order, or use a higher-level consumer. Subscribe immediately after
registration and call Query::destroy when finished; dropping a handle does not
unregister it. Db and the Cluster coordinator are !Send and stay on one thread.
§Storage and schema
Fresh embedded databases use ordinary WAL. Existing WAL/WAL2 files retain their
mode; OpenOptions selects a journal explicitly. Foreign keys are enforced by
default. Register every user table that a transaction can write, including cascade
targets. Captured foreign-key cascades become ordinary row changes.
Triggers remain unsupported on registered tables, and generated columns are rejected. Use the schema DDL methods before registration. To change a registered table’s shape, close the runtime, migrate the database, then reopen and register it.
Number-domain INTEGER cells must round-trip exactly through f64; admission and
capture reject values that would round. The storage layer can preserve exact
BIGINT/INT64 cells, but maintained queries reject a required exact-integer
column. See the schema guide for surface limits.
use rindle::table;
use rindle_replica::{Db, QueryId};
let db = Db::open("app.db")?;
db.exec_ddl("CREATE TABLE IF NOT EXISTS issue (id INTEGER PRIMARY KEY, title TEXT NOT NULL)")?;
db.register_table("issue")?;
let query = db.query(QueryId(1), table("issue").build())?;
query.subscribe(|update| println!("{update:?}"));
let mut tx = db.write()?;
tx.exec("INSERT INTO issue (title) VALUES ('First live query')", &[])?;
tx.commit()?; // subscribers run after the SQL commit
let count: i64 = db.read(|conn| conn.query_row("SELECT count(*) FROM issue", [], |row| row.get(0)))?;
assert!(count >= 1);
query.destroy();Modules§
- schema_
envelope - The replicated-SQL schema envelope: what SQLite objects the public surface accepts, how a
table is introspected into
ReplicatedTableSchema, and how a DDL/migration batch is applied under the registered-table guard with an audit trail. - session
- The shared interactive-mutation-session engine (REPLICATOR-INTERACTIVE-TXN-DESIGN.md §4).
- sql
- Shared SQL statement surface used by both the read follower and the write master.
- wire_
json - The replica’s JSON wire surface: the shared write-plane codec re-exported wholesale,
plus the two renderings that read the ENGINE’s own types and therefore cannot live in
the write plane — the flat-change / wire-schema dialect every native host ships to
its client (
flat_changes_to_json,wire_schema_to_json) and the SSR assembled snapshot (assembled_snapshot_to_json). - writeplane
- The shared write-plane machinery for the public one-shot SQL surface
(
303-PUBLIC-SQL-COORDINATOR-EXTRACTION-PLAN.md). PR 0 landed two layers, PRs 1–4 the coordinator’s batch, DDL, script, and migration slices:
Structs§
- AggTable
- A synthetic aggregate base table the server ships in place of an aggregate’s child
rows: the reduce’s
(group_key…, value)output, given a base-table home on the client (§3.2). Columns are[child_field…, "count"](mirroring the engine’sagg_relationship_reldef/Reduce::count_byoutput), the leadingkey_lenof which are the group key (and the PK). Derived purely from the AST — there is no such table in the DB — soagg_table_schemasfeeds the publisher’shello+ PK map for it. - Analyze
Join - One flippable EXISTS join’s routing decision.
- Analyze
Plan - The chosen plan — both layers (
ANALYZE-QUERY-DESIGN.md§3.1). - Analyze
Report - The full analyze report for one query — the
/analyzeresponse body. - Analyze
Source - The numbers for one source leaf — the row the CLI renders per table.
- Analyze
Spec - Everything one standalone
analyze queryrun needs, snapshotted off the live engine so the run itself can happen on any thread: the DB path, the registered tables, and the engine configuration. Built byCluster::analyze_spec; consumed byanalyze_standalone. Plain owned data (Send), no engine handles. - Analyze
Timing - Per-phase wall-clock of the throwaway run (
ANALYZE-QUERY-DESIGN.md§3.2).hydratedominates — it drives every source fetch and therefore all the scan/emit counting. - Analyze
Totals - Query-wide totals.
outputRows(final deduped IVM output) vs.emitted(rows sources pushed in) is a third useful gap — a big spread means the pipeline filters/joins a lot above the sources. - Base
Column - A column in a
BaseTableSchema. - Base
Table Schema - One base table’s externally-described schema (name + ordered columns with types + PK column
names), the shape
/schemaserves for client codegen (DRIZZLE-MIGRATIONS-DESIGN.md §6.2). - Cluster
- The multi-threaded replica handle (cheap
Rcclone). Single-thread-use on the coordinator side; a server runs it on a dedicated owner thread and continuously drains theClusterEventchannel returned byopenon whatever thread it likes — but it must never STOP draining while writes flow (the channel-out is bounded; seeopen’s draining contract). - Cluster
Consumer - The cluster-backed consumer (coordinator side).
!Send— lives on one thread; the drain pushes batches/progress/faults into the sink from its own thread. - Cluster
Mutation Write - One server-side mutation’s open cluster transaction (see
ClusterConsumer::begin_mutation). Dropping without committing rolls back. - Cluster
Write Txn - An open write transaction on the cluster’s single writer connection. Run SQL
with
exec/exec_batch;commitruns the snapshot/commit handshake. Workers can deliver provisional changes while SQL is still running. Dropping rolls back SQL; if chunks were already streamed, affected queries fault and need a new registration. SeeClusterEvent. - Commit
Info - What one committed transaction did, beyond its data effects: the new global tx id
(the commit version
cvthe optimistic protocol stamps on outgoing batches). Moved to the apply plane (the shared write-transaction state machine mints it — design 309); re-exported here sorindle_replica::CommitInfois unchanged. What one committed transaction did, beyond its data effects: the new global tx id (the commit versioncvthe optimistic protocol stamps on outgoing batches). Clientlmidadvances are NOT reported here —_rindle_client_mutationsrows ride the capture like any data and reach each client through its own system query (§8.2). - Db
- A single-thread SQLite runtime with live queries. Clones share the same writer,
engine, and registrations through
Rc; they do not create independent databases. This handle is!Send. Query and write handles keep the shared runtime alive. - DdlActions
- The destructive DDL actions observed so far (design 227, third review pass): which
main-schema tables were DROPPED or ALTERED, reported by SQLite’s authorizer as each statement was prepared. This is the real parser’s verdict, so comments between tokens, script-valued slots, quoting, and schema-qualified names are all handled by construction — the lexical scanners this replaced were evadable by each in turn. - DdlApply
Report - The full, ordered apply report of one
ddlentry. Persisted — as JSON, in the same transaction as the entry’s idempotency marker — so a replay (an in-connection retry after a later step failed, or a crash before cursor advancement) re-drives bounce decisions from durable truth instead of observing nothing. Desired-index effects normally commit in this same transaction; old reports are retained for conservative compatibility repair. - DdlStep
- One executed statement of a
ddlentry plus the destructive actions the authorizer attributed to it. Order is the EXECUTION order — load-bearing for the desired-index bookkeeping (design 227 fourth review pass): inDROP TABLE t; CREATE TABLE t; CREATE INDEX ix ON tthe index belongs to the final table, so its recording must follow the drop’s purge, not race it. - Derivation
Pool - The worker-pool seam for an external writer. See the module docs. Single-thread-use,
like
Cluster: the pool handle lives on the writer’s thread (the hooks fire there), and onlySenddata crosses to the workers. - Drain
- The running drain: owns the two worker/forwarder/loop threads. Dropping (or
shutdown) stops the loop; the forwarder exits when the cluster’s channel-out closes (i.e. when theClusteris dropped). - Drain
Handle - The coordinator-side handle: a cheap clone of the control sender. Lives on the Node main
thread; its methods are called from the napi
Dbsurface as queries are registered/destroyed, connections come and go, and commits land. - Engine
- The per-thread IVM unit (see the module docs). Internal to the
Db/Clusteropeners, and public via the crate’s embedded-engine seam (design 308): a host that owns its own write path — the SQLite extension’s commit hook — constructs one over a derivation connection, registers tables/queries, and drivesapply_batchitself. - Family
Extraction - A parameterized query family’s identity and template (design 310 §3), re-exported from
rindle-wireso a daemon groups subscriptions with the same types the engine binds on. The result of a successful extraction: the family the query belongs to, the template its pipeline compiles from, and the binding this query is. - Family
Key - A parameterized query family’s identity and template (design 310 §3), re-exported from
rindle-wireso a daemon groups subscriptions with the same types the engine binds on. Every daemon-side input that can affect the emitted rows of a family: the same four fields asQueryKey, with the canonical template bytes in place of the canonical AST bytes. - Family
Template - A parameterized query family’s identity and template (design 310 §3), re-exported from
rindle-wireso a daemon groups subscriptions with the same types the engine binds on. The compiled half of a family: the stripped AST the builder lowers, plus the names of the holed columns (the partition key, resolved toColIds by the builder) and each hole’s position among the original top-level conjuncts (whatinstantiateneeds to be an exact inverse). - Foreign
KeyAudit - The result of a
foreign_key_checkpass. - Foreign
KeyViolation - One row of
PRAGMA foreign_key_check: a child row whose declared reference has no parent. - Initial
Snapshot Column - Initial
Snapshot Commit - Initial
Snapshot Identity - Initial
Snapshot Store - The single SQLite owner for an initial snapshot.
PhantomData<Rc<()>>makes the lifecycle explicitly thread-confined even on platforms where rusqlite’s connection is movable. - Initial
Snapshot Table - Join
Precheck Report - Per-registered-query bookkeeping in the shared graph: the [
PipelineManifest] (the exact node/storage slots its build created, for teardown) and the caller’s opaqueQueryIdtag. Keyed by the query’s change-sinkNodeId. The engine does nothing with the tag (no de-dup / no refcount — that is the caller’s concern); it is retained for correlation/observability and echoed back via theQueryhandle. WhatEngine::join_precheck_reportreturns: the join membership pre-check’s bounds and per-query join states on one engine (design 311 §8 inspection). - Maintenance
Options - Which steps a maintenance pass performs and the thresholds that gate them.
Defaultis the server’s tuning: optimize on, reclaim up to 1000 freelist pages once ≥1000 have built up (~4 MiB at a 4 KiB page), passive checkpoint on. - Maintenance
Report - What a maintenance pass actually did — returned for observability/tests. A pass that ran while
a write transaction was open reports
skipped. - Mutation
Envelope - The upstream wire envelope (§8.1): one named-mutator invocation. Mutations are
totally ordered per client by
mid; the wire carriesname+args, never code. - Mutation
Outcome - What
Db::apply_mutationsdid: every transaction it committed (in order — applied mutations and lmid-only failure commits alike, each with itscrate::CommitInfo). Duplicate (already-processed) envelopes commit nothing. - Mutation
Reject - Why a mutator refused a mutation (permission/validation/SQL failure). Converts from
the common error shapes so
?works inside a mutator body. - Mutator
Registry - The server’s registry of named mutators (§4.2). One of the two registries (the client’s optimistic twin lives in the client engine); the wire only carries names, so sharing mutator code is a deployment choice, not a protocol mode.
- Node
Data - An owned, fully-materialized node: a row plus its relationship subtrees (keyed by
relationship slot). Re-exported from the engine (where it is
CaughtNode). A fully-materialized node: the row cells plus eagerly drained relationships. The comparison unit for a fetch/push assertion, and the owned node aGraph::add_change_sinkconsumer receives. - Normalize
Fold - The server-side serializer (§4): one per registered normalized query. Fold each
committed transaction’s
CaughtChanges withfoldto get that tick’sNormalizedOps. The footprint persists across ticks; the snapshot is simply the first fold over the hydrate batch (every row 0→1 ⇒ allAdds). - 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
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. - Open
Options - Everything an opener can be configured with, in one struct-with-
Default— so every combination (planner × operator storage × journal) is expressible without a ladder of positionalopen_with_*rungs. Taken byDb::open_with,Cluster::open_with, andClusterConsumer::open_with; the named rungs (open,open_with_planning,open_with_journal, …) are conveniences that fill one field each. - Pool
Gate - The commit-verdict gate, held between “all workers pinned + pushed” and the host
COMMIT’s outcome. Releasing it sends each worker its terminal
TxFinishwith the verdict inline; dropping it without either releases the workers as an abort (no worker hangs). - PoolTxn
- An in-flight transaction across the pool: every live worker holds a pinned pre-commit
snapshot, awaiting pushed chunks and the terminal gate. Dropping it without
finishreleases the workers’ snapshots as an abort. - 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. - Progress
Tracker - See the module docs. Single-thread (
Db) semantics; one instance per server. - Public
Authorizer Guard - Query
- A registered live query. Subscribe to receive its raw change events.
- QueryId
- A caller-supplied opaque tag for a registered query, echoed back via
Query::id. The engine never interprets it — it does no id generation, hashing, de-duplication, or refcounting. Identity and de-dup of “the same query” are the caller’s concern: a layer that materializes/keeps each query’s state can map its own ids toQueryhandles and reuse them, which is exactly the place that owns the per-subscriber hydration the engine’s raw change stream deliberately does not. Pass any scheme that suits you (a content hash, a uuid, a counter). - Read
Conn - A read-only connection for the daemon’s raw-read path. See the module docs for why this is separate from the coordinator’s read-write reader.
- Replicated
Column - One column accepted by the replicated-SQL schema envelope.
- Replicated
Table Schema - A persistent ordinary table accepted by the replicated-SQL schema envelope.
- Room
RowConflict - One CAS miss: the row’s authoritative current image (
None= absent). - Shared
Query Info - The query-wide pieces a server needs to frame each subscriber’s
hellofor a shared query (seeClusterConsumer::register_shared_query): the deterministic table set and itsnormalized_fp. The footprint fold itself lives on the drain thread. - Source
Head - Persisted source accounting at the applied cursor. Portable bases carry this so lag/change-count stamps resume from restored history instead of zero.
- SqlColumn
- SqlRead
Rows - The result of a raw parameterized read (
ReadConn::read_sql): the result columns in order and each row’s cells (mapped from their raw SQLite storage class). The caller owns how to render this — e.g. zippingcolumnswith each row into keyed objects. - SqlStatement
Request - Statement
Result - Storage
Report - What
Engine::storage_reportreturns: how much operator scratch state one registered query’s pipeline holds right now (design 310 impl plan D5’s leak probe, lifted to the cluster boundary — seeCluster::__test_storage_entries). - Stored
Source Checkpoint - Table
Meta - What a table registration records (the column order + PK columns drive Mutation→SQL; the
per-column
ColTypefeeds schema codegen via/schema). - Table
Node - The query’s table tree: each frame’s base table plus, per relationship slot (in the
query-local
RelIdorder), the child frame’s tree. It is the path-free replacement forPathSeg: folding aCaughtChange::Childdescendschildren[rel]to learn the child’s table, then throws the parent row away. - Table
Schema - The discovered shape of a registered table: columns in
cid(== [ColId]) order plus the primary-key column ids. FeedsTableSource::try_new.Cloneso the coordinator can fan one discovered schema out to every worker (it isSend:ColumnDefisBox<str>+ scalars). Public via the crate’s embedded-engine seam (design 308): a host that drivescrate::Enginedirectly discovers withdiscoverand registers the result. - 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). - TxId
- A global, monotonic, durable-with-the-data transaction id.
- WalCheckpoint
- The three integers a
PRAGMA wal_checkpointreturns. Moved to the apply plane (ApplyStore::checkpoint_truncateis the TRUNCATE flavor’s home — design 309); re-exported here sorindle_replica::WalCheckpointand the maintenance report are unchanged. OnePRAGMA wal_checkpoint(TRUNCATE)outcome (ApplyStore::checkpoint_truncate). - Write
Txn - An open write transaction on the single writer connection. Run ordinary SQL with
exec/exec_batch; the preupdate hook captures row deltas.commitderives against a read-only pre-commit snapshot and batch overlay, commits SQL, then calls subscribers. Dropping without committing rolls back and delivers no events.
Enums§
- Change
Event - An owned, fully-materialized change event off the pipeline — the raw delta the
engine emits (re-exported from the engine, where it is
CaughtChange). Nested relationships are carried inNodeData::relationships. A caught downstream change. Mirrorscatch.tsexpandChangeoutput: anEditcarries only the two rows (no node,catch.ts:104-109); aChildcarries the parent row, the relationship slot, and the nested change (catch.ts:110-118). - Cluster
Event - Events from a
Cluster’s bounded channel. Registration emits a committedHydratedbaseline. LaterChangedslices can arrive before the transaction commits. Buffer them untilProgressedfrom the worker that hosts the query. For a combined result across workers, wait for every relevant worker. - ColType
- A column’s declared type (from the host schema) — drives SQLite affinity at DDL time.
- DdlMigration
Error - A standalone authority’s checked DDL apply can fail either in the cluster/schema machinery or in the shared write-plane policy checks. Keeping the latter typed preserves stable migration error codes all the way to the host’s HTTP renderer.
- Fault
Cause - Why a query faulted (
ClusterEvent::Faulted) — the classification the host’s mode machine consumes (FOLLOWER-LAG-SHED §6.6 item 3: today the error’s identity died inside the worker as a barefaulted = true, making a deadline bail indistinguishable from an ordinary derive fault). - Foreign
Keys - Whether SQLite enforces declared foreign keys on one connection.
- Initial
Snapshot Open - Journal
Mode - The WAL-family journal mode an opener ensures on a fresh file (design 306 D5).
Under the default
Walrequest an existingwalorwal2file keeps its mode (D3 — the opener accepts both). An explicitWal2request is a requirement, not a preference: the opener attempts the conversion and fails loudly if the file does not end up in wal2 — the daemon’s fleet contract (followers, backups) must never be silently unhonored. - Mutation
- One positional mutation (cells aligned to the table’s column order).
- Normalized
Applied - The outcome of applying a
NormalizedBatch. - Normalized
Op - One normalized change. Self-identifies by table name — the client routes each row
to a base table directly, with no tree path and no slot map (§3). Rows are positional
(aligned to that table’s column order).
OwnedValueis serializable because the crate enablesrindle’sserdefeature, so the op serializes for the wire / oracle directly. - 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). - Operator
Storage - Where stateful operators (
take/cap/reduce) keep their scratch state. - Replica
Error - Everything the write plane — and the
rindle-replicaIVM half above it — can fail with. Wraps the engine’s own errors verbatim (Rindle/Build) and adds the wrapper-level faults (connection open, capability asserts, unsupported columns). - Room
Flush Outcome - The outcome of a CAS-guarded room flush (
ClusterConsumer::commit_room_flush). - SqlArgs
- Statement
Class - Statement
RunError - Update
- A query baseline or incremental changes. A
Hydratedcontains the full result asAddevents and replaces any previous baseline.Changedcontains deltas to apply in order. Delivery depends on the runtime:
Constants§
- APPLIED_
DDL_ TABLE - The follower’s DDL idempotency journal: one row per applied
ddlentry, keyed by the migration id (or the entry offset when a source ships none). Unregistered bookkeeping like the offsets table — never captured/fanned. A crash-window replay (theddlre-delivered before its cursor advanced) dedups against this BEFORE re-applying, so an already-applied reshape is skipped exactly rather than re-run-and-inferred-from-the-error (seeApplyStore::exec_ddl_with_marker). - CDC_
BOOTSTRAP_ TABLE - CLIENT_
MUTATIONS_ TABLE - The replicated bookkeeping table carrying each client’s high-water mutation id.
Single-
_prefixed: captured by CDC (unlike__replica_meta) and hosted by the engine like any base table — each client’s lmid flows to it through its own one-row system query, transactionally with the data (§8.2). - FOREIGN_
KEY_ AUDIT_ ROW_ CAP - Default cap for
foreign_key_check. A broken store can violate on every row, and an audit that answers “yes, and here is where it starts” is worth more than one that tries to materialize millions of rows. - OUTCOME_
RETENTION_ LMIDS - Retention bound for
ROOM_MUTATION_OUTCOMES_TABLErows, by lmid distance — never by time (Slice I-ii). When a room flush advances a(doc, client)ledger row tolmid, rows withmid ≤ lmid − Kprune in the same transaction.K = 512mirrors the room shell’s per-client recorded-outcome FIFO cap (MAX_RECORDED_OUTCOMES_PER_CLIENT = 512,packages/room/src/shell.ts) — the two ends of the outcome-resolution surface degrade at the same depth. The accepted loss class is the H-v one: a pruned mid reads asappliedthrough the daemon, exactly as an evicted map entry re-answers with silence on the room socket; a client is only ever that far behind its own ledger with a backlog ≥ K in flight. - READ_
ROW_ CAP - Max rows a single raw read may return before it is rejected. A hard ceiling against an
unbounded result set exhausting memory; well above any reasonable one-shot read (add a
LIMIT/ narrow the query if you hit it). - READ_
TIME_ BUDGET - Wall-clock budget a single raw read may run before it is interrupted. Generous — an interactive API read should finish in milliseconds; this only fences a pathological query (cartesian blow-up, runaway recursive CTE) from wedging the reader.
- READ_
VM_ BUDGET - Approximate SQLite VM-instruction ceiling for one read statement. The progress handler is the in-band backstop to the wall-clock watchdog and is reset after every statement.
- ROOM_
CLIENT_ MUTATIONS_ TABLE - The domain-scoped ledger for room-flush lmid co-commits
(RINDLE-REALTIME-QUERY-ENABLEMENT-DESIGN.md §7.1). Keyed by
(doc, client_id)— one gapless mid stream per domain, one ledger row per domain — so a room flush and a slow-path daemon mutation for the SAME client never collide on a shared row (the Rev 1 data-loss bug, §8.5’s “ledger isolation” invariant).CLIENT_MUTATIONS_TABLEstays exclusively the slow-path stream; room flushes retarget here. - ROOM_
MUTATION_ OUTCOMES_ TABLE - The durable twin of the H-iv-b
mutationOutcomeframe ({mid, kind, reason?, name?, args?}), keyed(doc, client_id, mid)— the §4 lifecycle’s outcome-resolution surface for THE NAMED INVARIANT: never retire a room-domain entry off a daemon-carried lmid without outcome resolution (§3.3/§7.5). After downgrade the room socket that ordered outcome-before-ack is gone, so non-applied verdicts must be readable through the daemon subscription plane like the §7.1 ledger row. Rows are written by Slice I-ii’s flush split — this slice only creates + registers the table; an absent row for a covered mid reads asapplied(only non-applied outcomes are recorded, matching the room shell’s recorded-outcome map). - ROOM_
PLACEMENT_ TABLE - The room placement-fence table (
RINDLE-REALTIME-DESIGN.md§2.5): one row per doc, bumped by every claim. A flush carrying an epoch below the current claim is fenced — validated with the apply on the single-threaded engine, so the check and the commit are atomic. Unregistered bookkeeping likeSOURCE_OFFSETS_TABLE. - ROOM_
WATERMARK_ TABLE - The §4.2 downgrade fence:
(doc, flush_seq), co-committed monotonically in every room flush’s transaction (ClusterConsumer::commit_room_flush). Cross-authoritycvs are incomparable, so the fence is data that RIDES THE ECHO: a downgraded client keeps its frozen ghost source until its daemon subscription deliversflush_seq ≥ finalFlushSeq— proof the store it fell back to holds the room’s final flush, whatever the authority shape (single daemon / lagging read-follower / PG). - SCOPE_
SESSIONS_ TABLE - The §4.1 occupancy table (RINDLE-REALTIME-QUERY-ENABLEMENT-DESIGN.md): one row per
(scope, session), upserted by the api-server on every labeled lease mint/renewal and aged out lazily byexpires_at(mark/refresh/age-out — lease expiry needs no hook anywhere). The row delta IS the upgrade doorbell: a solo client’s only live connection is its daemon subscription, so the 1→2 wake signal must materialize as a row in the store it is subscribed to — which is whyDb::enable_realtime_lifecycleregisters this table rather than just creating it. - SOURCE_
OFFSETS_ TABLE - The durable per-change-source cursor table (CHANGE-SOURCE-DESIGN.md §4). One row per
source; the offset upsert is co-transactional with the batch it covers
(
ClusterConsumer::commit_normalized_with_offset). Daemon bookkeeping — NOT registered for capture (same posture as_rindle_sql_outcomes; the consumer’s own resume position is meaningless on any other host). - SOURCE_
OFFSET_ WHOLE_ RUN - The
chunk_seqsentinel meaning “the whole run at thisoffsetis durably applied” — the common case (every pure-row run and every run-boundary commit). A genuine value< thisis a mid-run checkpoint(offset, chunk_seq)left by the commit-at-DDL-boundary follower (RELAY-DDL-DESIGN.md §6.6): chunks0..=chunk_seqofoffsetare applied, the tail is not. The resume/dedup compare is the keyset(offset, chunk_seq), with the incomingbegin(R)treated as(R, WHOLE_RUN)— so a whole run sorts at/above any of its mid-run positions.i64::MAXis safe as a sentinel:chunk_seqis a 0-based within-run ordinal (one per spilled ≤CHUNK_ROWSchunk), so a real value reachingi64::MAXis physically impossible. Mirrors the relay fan-out’sScanPos“past the end of this run’s chunks” sentinel (rindle-replicator). - SQL_
BIND_ LIMIT - SQL_
BIND_ VALUE_ BYTE_ LIMIT - SQL_
RESULT_ BYTE_ LIMIT - SQL_
RESULT_ ROW_ LIMIT - SQL_
TEXT_ LIMIT - Named v1 request limits. They are deliberately conservative relative to SQLite’s own limits; exceeding one is a request/result error, never silent truncation.
Traits§
- Drain
Sink - The sink the drain delivers finished output to. Implemented by the napi layer over a
ThreadsafeFunction(→ JSonEvent) and by tests over a collector. Called on the drain thread, so keep each call cheap (marshal + hand off). - Mutation
Sql - The SQL flavor of the design’s
MutationTx(§4.2) — the abstract write handle a server mutator runs against. Statements execute inside the mutation’s own open write transaction, andqueryreads through the same connection, so a mutator sees its own uncommitted writes and the effects of every lower-midmutation — exactly what read-dependent mutators need (§4.1).
Functions§
- agg_
table_ name - The synthetic base-table NAME for a relationship
countaggregate (§3.1): a content hash of the aggregate’s definition — child table, kind, the group key (correlation child fields), and the childwherefilter — so two queries with the same definition share one table (cross-query refcount on the client) while a different filter gets a different table (no(table, pk)collision). The parent correlation field is excluded: the count for a given group key is the same whichever parent joins it. - agg_
table_ schemas - Collect a synthetic
AggTablefor every relationshipcountaggregate inast, recursively (a nested aggregate under a materializedrelatedis included). The caller (the replica consumer / the publisher’s schema list) advertises these alongside the real base-table schemas so the client can register + validate them. - analyze_
standalone - Run one
analyze queryCOLD on the calling thread over a private connection tospec.path, touching no engine, no worker, and no live statement cache. - build_
mutation_ sql - Build the SQL + bound params for one positional mutation against a table’s
columns+ PK column indices (the controlled writer is the same SQLite shape on every path). - 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. - canonical_
migration_ checksum - Lowercase SHA-256 of the compact JSON encoding of a normalized statement vector.
- cas_
cell_ matches - The CAS cell comparator: [
values_identical] (total,null == null, one number domain), widened over SQLite’s boolean storage — a wiretruematches a stored1(BOOLEAN columns round-trip through INTEGER affinity). - classify_
statement - Classify by the first executable top-level keyword, after stripping comments and accounting
for CTE bodies.
sqlite3_stmt_readonlyremains an execution-time cross-check. - create_
table_ ddl CREATE TABLE IF NOT EXISTSfor a typed column spec + PK column indices. Shared by the single-thread and cluster-backed cores so their schemas are identical.- decode_
wire_ value - Decode one v1 SQL wire value. Integers use a tagged decimal string; finite REALs use JSON numbers and infinities use the tagged float form. There is deliberately no BLOB tag in v1.
- discover_
table_ schema - Discover a table’s columns + primary key via
pragma_table_info. - encode_
wire_ value - ensure_
unique_ pk_ index - Ensure the table has a UNIQUE index covering exactly the PK columns, which
TableSourcerequires for row-identity point lookups. Idempotent. - extract_
family - A parameterized query family’s identity and template (design 310 §3), re-exported from
rindle-wireso a daemon groups subscriptions with the same types the engine binds on. Extractast’s family, if it can join one (module docs).Ok(None)⇒ the query cannot join any family and falls back to itsQueryKey— today’s path, byte for byte.Erronly on the (practically unreachable) serialization failureQueryKeyshares. - foreign_
key_ check - Walk every declared foreign key and report violating rows — the opt-in audit.
- 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). - install_
public_ authorizer - Install the reserved-object authorizer on
connfor the lifetime of the returned guard. The authorizer denies any statement that touches a reserved_rindle_*/hct_*/sqlite_*object (and any connection-local action — ATTACH/PRAGMA/DETACH), matching the fail-closed rule the public/v1/sql/*execute path already enforces. Exposed so callers outside this module (the mutation facade’s raw statement executor) can bracket a bareconn.executewith the same guard the run_statement path uses.classis the statement class being run. - introspect_
replicated_ table - Introspect and validate one persistent application table.
- introspect_
schema_ envelope - Introspect every public application table and validate the complete replicated SQL schema envelope. Internal bookkeeping objects remain excluded.
- is_
internal_ schema_ name - Internal bookkeeping objects are not part of the public SQL schema envelope.
- normalize_
migration_ statements - Normalize migration carrier slots by splitting each one and flattening retained statement bytes in order. Delimiters and trimmed outer whitespace are not identity; comments and whitespace retained inside each statement remain byte-significant. A slot-less migration is a valid version-only journal entry; a supplied slot must still contain a statement.
- 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. - open_
journal - The journal-aware connection-opening ritual (journal negotiation + the hardening/
planner pragmas +
regexp) moved to the apply plane (design 309): the headless apply store and every engine connection here must open IDENTICALLY, so there is one implementation. Re-exported at the historical paths —crate::parallel::open_journalis the embedded-engine seam’s public opener (design 308). Open one read-write connection (mirrorsDb::open’s per-connection setup). Used for the apply store’s writer, the cluster coordinator’s writer, and each IVM worker. - restore_
bootstrap - Restore a follower’s data file from a self-describing snapshot — the §10.4 bootstrap path,
taken by a brand-new follower OR one that fell off the master’s retention window
(
cursor-too-old,CHANGE-SOURCE-DESIGN.md§10.2). The snapshot is an ordinary, consistent SQLite follower snapshot (including a canonical portable base materialized byrindle-backup), so restoring is: - restore_
bootstrap_ with_ journal restore_bootstrapwith an explicit targetJournalMode. The snapshot arrives in rollback (delete) mode; the hop below passes throughdeletebefore requesting the target mode (wal2 cannot be entered from wal directly — §10.3 — and the symmetric hop is harmless forwal), then asserts the file actually landed in it.- rewrite_
aggregates - Rewrite an AST for a client engine reading synced aggregate tables — the Rust twin of
TypeScript
rewriteAggregates(packages/normalized/src/agg-table.ts), byte-for-byte in its choice of table name because both callagg_table_name. - rewrite_
aggregates_ with_ local rewrite_aggregates, with the L1 local-table carve-out (201-LOCAL-ONLY-TABLES-DESIGN.md§5.2): a count over a tableis_localaccepts is a native IVM reduce with no server-authoritative__agg_*base, so it is left alone — rewriting it would point the relationship at a synthetic table nothing ever feeds.- run_
ddl_ statement - Execute a DDL-class statement after the host has established the migration transaction and quiesced other writers. Keeping this entry point explicit prevents ordinary transaction callers from accidentally admitting DDL.
- run_
statement - Prepare through rusqlite’s per-connection cache, bind either argument form, step through every row (including DML RETURNING), and materialize the common result shape.
- set_
foreign_ keys - Put
conninpostureand verify it took. - split_
migration_ file - Split one migration-file carrier, treating an exact Drizzle breakpoint physical line as an explicit boundary before passing every carrier through the shared SQL scanner. The breakpoint comment itself is not canonical migration content.
- split_
sql_ script - Split an ordered autocommit script at top-level semicolons. Delimiters inside quoted values, identifiers, and comments are preserved. Empty/comment-only segments are ignored.
- split_
sql_ script_ allow_ empty - The migration-file variant of
split_sql_script. It uses the same quote/comment/trigger aware scanner but permits a comment-only carrier slot so a whole file can be flattened without inventing a second SQL lexer. - sql_
cell_ to_ owned - One result cell from its raw SQLite storage class — the storage-class twin of
decode_wire_value’s JSON one. No engine coercion (a caller here is writing SQL, not feeding the pipeline); BLOB has noOwnedValueform. Shared by the single-thread writer, the parallelClustermutator-read path andReadConn— and re-exported (sql_cell_to_owned) for the replicator write-master’s session reads, which MUST convert cells identically or the two masters’/mutate-session/queryreplies drift (REPLICATOR-INTERACTIVE-TXN-DESIGN.md §7). - statement_
is_ insert - Whether the statement action can update SQLite’s connection-local last-insert-rowid counter. The host still checks the target table’s WITHOUT ROWID metadata before reporting it.
- table_
tree - Derive the
TableNodetree forast. A pure function of the AST — it needs only table names (all present inline: the rootast.table, each subquery’stable), never the source schemas, so noresolveclosure. Mirrorsview_schema’s slot walk exactly (normalize the frame, take [query_local_slot_names], resolve each slot name to its subquery, recurse) but carries the table where the schema carries the alias. - 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_
schema_ envelope - Validate the complete replicated SQL schema envelope without retaining its introspection result.
- value_
type_ of - Map a declared SQLite column type to the engine’s [
ValueType], following SQLite type-affinity rules (with BOOLEAN/JSON special-cased, and the exactBIGINT/INT8declarations mapped to the exact-i64 plane — design 226 §4.1). ReturnsNonefor BLOB and untyped columns — the engine’sOwnedValuehas noBlobvariant, so we reject rather than silently null them.
Type Aliases§
- Binding
- A parameterized query family’s identity and template (design 310 §3), re-exported from
rindle-wireso a daemon groups subscriptions with the same types the engine binds on. A family member’s parameter tuple: the canonical literal of each holed conjunct, in hole order. A [CanonKey] (impl plan D1) — the same type the engine’s partition membership test and the daemon’s per-partition demux key by, so the three agree by construction.Send + Sync(the payloads areArc<str>), so it crosses worker command channels. - ConnId
- A consumer-assigned connection identifier (one per subscriber/WebSocket). The drain
groups queries by connection for the
cv_min/poke computation; it never interprets it. - PkMap
- Table → primary-key column indices (into that table’s row), supplied at construction.
- 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). - Server
Mutator - A server-side mutator: authoritative, run once, against the open write transaction.
May read its own transaction’s uncommitted writes via
MutationSql::query(read- dependent mutators see the current base, §4.1). AnErrrejects the mutation. - SubId
- A subscription id — one per attached subscriber (the routing identity the
DrainSinkdelivers on, surfaced as aQueryIdin the sink callbacks). Distinct from the engine query id: many subscribers can share one engine query (dedup), each with its own epoch / seq cursor / connection route.