FlashQL Sync
FlashQL sync is the orchestration layer for sync-enabled views.
If you have already read Federation, Materialization, and Realtime Views, this page is the operational companion:
- that page explains the model
- this page explains the control surface
What db.sync manages
db.sync works over FlashQL views whose persistence is one of:
materializedrealtime
origin views participate in the wider federated graph, but they are not themselves sync jobs in the same way.
The main entry point: sync.sync()
The central design decision in FlashQL sync is that there is one idempotent entry point:
await db.sync.sync();This is meant to reduce application-side bookkeeping.
You should be able to call it:
- on startup
- on reconnect
- when your app is unsure whether sync is already active
And get the correct reconciliation behavior without having to memorize a dozen separate lifecycle APIs.
What sync.sync() actually does
For selected sync-enabled views, sync.sync() may:
- discover candidate views
- ensure sync job rows exist
- materialize local copies when needed
- start realtime jobs
- resume sync work after reconnect
It also coalesces overlapping runs internally, so reconnect storms do not multiply the full sync pass unnecessarily.
Selector support
Like other LinkedQL selector-based APIs, sync operations accept:
'*'
{ public: ['users_cache', 'posts_live'] }Examples
await db.sync.sync();
await db.sync.sync({ public: ['users_cache'] });
await db.sync.sync({ public: ['posts_live'] });status()
Use status() to inspect the current sync state of matching views.
const status = await db.sync.status({ public: ['users_cache', 'posts_live'] });
console.log(status);Status records can include fields such as:
relation_idnamespacenamepersistencemodeenabledstateslot_idlast_seen_commitretry_countlast_errorupdated_at
Typical state meanings
idle: not currently runningsynced: a materialized copy was populated successfullyrunning: a realtime job is activefailed: the last attempt failed
stop()
Use stop() to halt selected realtime jobs.
await db.sync.stop({ public: ['posts_live'] });By default, stop() also disables the stopped realtime job until you explicitly resume it.
resume()
Use resume() to re-enable previously stopped realtime jobs.
await db.sync.resume({ public: ['posts_live'] });resume() re-enables the job and then routes back through the normal sync logic.
Practical startup and reconnect flow
This is the core pattern most local-first apps want:
await db.connect();
await db.sync.sync();
window.addEventListener('online', async () => {
await db.sync.sync();
});Why this works well:
sync.sync()is idempotent- overlapping calls are coalesced
- you do not need brittle application-side state flags just to avoid duplicate startup logic
Materialized vs realtime behavior
Materialized view
For a materialized view, sync typically:
- fetches the upstream rows
- replaces the local copy
- marks the job as
synced
Realtime view
For a realtime view, sync typically:
- ensures a usable local copy exists
- starts the upstream subscription
- updates the local mirror as upstream commits arrive
For reference-based realtime views, the local copy is bootstrapped and then maintained through WAL/changefeed updates.
Example: inspect, stop, resume
await db.sync.sync({ public: ['posts_live'] });
const before = await db.sync.status({ public: ['posts_live'] });
console.log(before[0].state);
// 'running'
await db.sync.stop({ public: ['posts_live'] });
const stopped = await db.sync.status({ public: ['posts_live'] });
console.log(stopped[0].enabled);
// false
await db.sync.resume({ public: ['posts_live'] });
const resumed = await db.sync.status({ public: ['posts_live'] });
console.log(resumed[0].state);
// 'running'What sync is and is not
This page describes the current sync layer accurately if you read it this way:
- sync manages local materialization and realtime mirroring of upstream relations
- sync works through explicit view definitions and job state
- sync is not a vague "magical eventual consistency" promise
That narrower, concrete framing is deliberate. It matches the code and the tests today.
