diff --git a/.releases/1.9/commits.md b/.releases/1.9/commits.md new file mode 100644 index 00000000..20869dcd --- /dev/null +++ b/.releases/1.9/commits.md @@ -0,0 +1,203 @@ +# Zero 1.9 Release Audit + +Status: public draft and product-doc updates complete; #6206 benchmarks deferred; smoke-test handoff blocked on a 1.9 canary. + +## Release Provenance + +- Release: `1.9.0` +- Monorepo: `/Users/chase/git/roci/mono` +- Monorepo remote: `git@github.com:rocicorp/mono.git` +- Docs repository: `/Users/chase/.worktree/zero-docs/1.9` +- Previous ref: `zero/v1.8.0` +- Previous SHA: `cdc02598f137ab4e071878f5674fdc716dbbc69d` +- Target ref: `maint/zero/v1.9`, frozen for this audit +- Target SHA: `ef892a123a11461e74a59a4b59ad310ba23180b3` +- Merge base: `2279e783edd94aaa20fdcc8e067860ad0c21d95b` +- Raw non-merge range: 39 commits +- Patch-equivalent commits already shipped in 1.8: 15 +- Unique 1.9 commits: 24 + +Commands used: + +```bash +git remote -v +git rev-parse zero/v1.8.0 maint/zero/v1.9 +git merge-base zero/v1.8.0 maint/zero/v1.9 +git rev-list --count --no-merges zero/v1.8.0..maint/zero/v1.9 +git log --reverse --oneline --no-merges zero/v1.8.0..maint/zero/v1.9 +git log --right-only --no-merges --cherry-mark --format='%m%x09%h%x09%s' zero/v1.8.0...maint/zero/v1.9 +git log --left-only --no-merges --cherry-mark --format='%m%x09%h%x09%s' zero/v1.8.0...maint/zero/v1.9 +git log --format='%H%x09%s%n%b' 2279e783edd94aaa20fdcc8e067860ad0c21d95b..zero/v1.8.0 +git show zero/v1.8.0:packages/zero-protocol/src/protocol-version.ts +git show ef892a123a11461e74a59a4b59ad310ba23180b3:packages/zero-protocol/src/protocol-version.ts +``` + +## Protocol Compatibility + +PASS. Both refs use sync protocol `51` and minimum server-supported sync protocol `30`. + +| Check | Zero 1.8 | Zero 1.9 target | Result | +| ------------------------------------ | -------: | --------------: | ------------------------------------ | +| Sync `PROTOCOL_VERSION` | 51 | 51 | Compatible | +| `MIN_SERVER_SUPPORTED_SYNC_PROTOCOL` | 30 | 30 | Compatible | +| Change-stream protocol | 6 | 6 | Compatible | +| DDL event protocol | 1 | 1 | Compatible; `newColumns` is optional | + +The required gate passes because the target minimum supported protocol, `30`, is less than or equal to the previous release protocol, `51`. No protocol migration or server-before-client deployment sequence is introduced by this range. + +## Backport Detection + +The 1.8 tag and 1.9 branch diverge at the merge base. The following target commits are patch-equivalent to maintenance commits already present in `zero/v1.8.0` and are therefore classified as already shipped: + +| Target commit | 1.8 commit | Subject | +| ------------- | ----------- | ------------------------------------------------ | +| `c263df101` | `31e48aa71` | z2s and NULL start handling (#6189) | +| `c6f4c9712` | `6c8b5e5a7` | Initial-sync metrics (#6191) | +| `a99e63093` | `ca6c2f3e9` | postgres.js disconnected-socket crash (#6193) | +| `d379d93cb` | `492b5c331` | Litestream backup and restore metrics (#6199) | +| `754be6b1d` | `3609d48af` | Node version for `availableParallelism` (#6198) | +| `bb8703753` | `4ef9e4ef0` | Catch-up subscriber flow control (#6186) | +| `3adc4383f` | `c2b5a7565` | API-server request metrics (#6203) | +| `e03cc5301` | `e91debd95` | CVR, WebSocket, and flow-control metrics (#6207) | +| `6cc6629ca` | `d711edbb2` | Metric naming alignment (#6209) | +| `596a9376b` | `dcbc5b5ea` | Remove SQLite quick-check (#6212) | +| `478711b76` | `d4258993d` | Aggregable lag metrics (#6214) | +| `89d57b460` | `f71c897c4` | Replication-slot metrics (#6210) | +| `89c48bbc2` | `832580d65` | Worker startup metric (#6208) | +| `ca40512bf` | `312a0f78f` | Release README changes (#6218) | +| `15fd7cdea` | `cdc02598f` | Export `MutatorResult` (#6223) | + +The previous-release side contains no additional `cherry-pick -x` trailers naming target commits beyond the patch-equivalent set above. + +## Commit Decisions + +| Commit | Category | Breaking? | Public impact | Decision and evidence | +| --------------------------------------------------------- | ----------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [`a59cf1a66`](https://github.com/rocicorp/mono/pull/6183) | skip | - | None; adds a SQLite change-log throughput benchmark. | Omit. Benchmark-only file `sqlite-change-log-ceiling.bench.ts`; no production path changes. | +| [`cb014857f`](https://github.com/rocicorp/mono/pull/6185) | fix | - | Preserves an own `__proto__` property in schema construction, CRUD maps, and materialized-view updates instead of invoking the legacy prototype setter. | Public candidate. Tests cover shared object helpers, schema builders, mutators, and IVM view updates. Scope wording narrowly because name mapping and several other dynamic-key paths lack end-to-end coverage. Existing affected schemas may get a corrected client-schema hash and normal resync. | +| [`5361250c2`](https://github.com/rocicorp/mono/pull/6182) | skip | - | Default-on shadow analysis detects query coverage and emits summary logs; it does not reuse results or change query execution. Adds `ZERO_ENABLE_QUERY_COVERING`. | Intentionally omit as internal rollout telemetry. Human review accepted the default's possible CPU/log overhead without promoting the control as a supported public option. Unit coverage is in `query-covering.test.ts` and config tests; no production overhead benchmark exists. | +| [`53194a166`](https://github.com/rocicorp/mono/pull/6190) | skip | - | Changes package version from 1.8.0 to 1.9.0. | Omit as release metadata. | +| [`c263df101`](https://github.com/rocicorp/mono/pull/6189) | skip | - | Fixed z2s `start` compilation and leading NULL cursor handling. | Already shipped in 1.8 as patch-equivalent `31e48aa71` and covered by the 1.8 release note. | +| [`c6f4c9712`](https://github.com/rocicorp/mono/pull/6191) | skip | - | Added initial-sync duration, phase, row, byte, chunk, and outcome metrics. | Already shipped in 1.8 as patch-equivalent `6c8b5e5a7` and covered by the 1.8 operational-metrics feature. | +| [`a99e63093`](https://github.com/rocicorp/mono/pull/6193) | skip | - | Prevented postgres.js writes through a socket after disconnection. | Already shipped in 1.8 as patch-equivalent `ca6c2f3e9` and listed in the 1.8 fixes. | +| [`34b204638`](https://github.com/rocicorp/mono/pull/6179) | skip | - | Adds a private Postgres-to-client throughput and latency harness. | Omit as benchmark infrastructure. Production-file changes only normalize imports. The harness may be temp-backported for #6206 measurements. | +| [`58861830f`](https://github.com/rocicorp/mono/pull/6192) | skip | - | None for developers or operators; parallelizes JavaScript CI jobs. | Omit as CI-only. | +| [`d379d93cb`](https://github.com/rocicorp/mono/pull/6199) | skip | - | Added Litestream backup, restore, validation, and subprocess metrics. | Already shipped in 1.8 as patch-equivalent `492b5c331` and covered by the 1.8 operational-metrics feature. | +| [`754be6b1d`](https://github.com/rocicorp/mono/pull/6198) | skip | - | Corrected Docker's Node version so default sync-worker sizing uses `availableParallelism`. | Already shipped in 1.8 as patch-equivalent `3609d48af` and listed in the 1.8 fixes. | +| [`48694d892`](https://github.com/rocicorp/mono/pull/6187) | fix | - | Retries missing replication-lag probes and stops `total_lag` from growing indefinitely when no report is received. Adds `lag_report_retries`; `last_total_lag` is no longer semantically distinct. | Include publicly, grouped with #6219. Tests cover retry and recorder behavior. Human review classified this as non-breaking and requires `otel.mdx`, `zero-cache-config.mdx`, and alert-migration guidance for operators relying on old gauge semantics. | +| [`bb8703753`](https://github.com/rocicorp/mono/pull/6186) | skip | - | Bounds catch-up subscriber backlogs and applies downstream flow control. | Already shipped in 1.8 as patch-equivalent `4ef9e4ef0` and listed in the 1.8 fixes. | +| [`ec7c5c678`](https://github.com/rocicorp/mono/pull/6197) | skip | - | Expands end-to-end fuzzing design and test coverage. | Omit as test-only. No shipped runtime behavior changed. | +| [`7d13c1b4b`](https://github.com/rocicorp/mono/pull/6196) | fix | - | Forces a CVR version bump when a same-hash query is rebuilt without another bump, ensuring changed rows reach clients instead of leaving stale results. | Public candidate. View-syncer and CVR integration tests cover missing-pipeline and row-signature drift cases and duplicate-delete avoidance. | +| [`3adc4383f`](https://github.com/rocicorp/mono/pull/6203) | skip | - | Added metrics for mutate, query, cleanup, and auth-validation API calls. | Already shipped in 1.8 as patch-equivalent `c2b5a7565` and covered by the 1.8 operational-metrics feature. | +| [`e03cc5301`](https://github.com/rocicorp/mono/pull/6207) | skip | - | Added CVR, WebSocket, and replication flow-control metrics. | Already shipped in 1.8 as patch-equivalent `e91debd95` and covered by the 1.8 stability-metrics feature. | +| [`6cc6629ca`](https://github.com/rocicorp/mono/pull/6209) | skip | - | Aligned metric names with product documentation. | Already shipped in 1.8 as patch-equivalent `d711edbb2`; no additional 1.9 behavior. | +| [`03223e310`](https://github.com/rocicorp/mono/pull/6211) | skip | - | None through supported exports; deletes an unused shared helper and tests. | Omit as an internal refactor. `shared` is private, the helper was not exported, and history shows no callers outside its tests. | +| [`42819059b`](https://github.com/rocicorp/mono/pull/6202) | fix | - | Bounds each zero-cache SQLite prepared-statement cache to 1,000 idle entries with LRU eviction, preventing unbounded retained statement state from varied query shapes. | Include as a non-breaking reliability fix. Unit tests cover the bound, LRU ordering, duplicate SQL instances, and in-flight statements. Human review accepted the fixed, non-configurable per-cache limit; avoid claiming immediate native-memory release because finalization remains GC-driven. | +| [`596a9376b`](https://github.com/rocicorp/mono/pull/6212) | skip | - | Removes a SQLite quick-check that was too slow in production. | Already shipped in 1.8 as patch-equivalent `dcbc5b5ea`; no additional 1.9 behavior. | +| [`86d531954`](https://github.com/rocicorp/mono/pull/6205) | skip | - | Adds fault-injection coverage for replication resumption after process, network, and acknowledgement failures. | Omit as resilience testing; validates existing behavior without a production change. | +| [`478711b76`](https://github.com/rocicorp/mono/pull/6214) | skip | - | Makes lag metrics aggregable across time and pods. | Already shipped in 1.8 as patch-equivalent `d4258993d` and covered by the 1.8 stability-metrics feature. | +| [`eac60e7c3`](https://github.com/rocicorp/mono/pull/6206) | performance | - | For sufficiently large or pathological updates, projects incremental-maintenance cost and rebuilds query pipelines when rebuilding should be cheaper, while allowing nearly complete updates to finish. | Performance evaluation deferred by human review. Synthetic tests verify reset thresholds, not end-to-end performance, so the public note makes no behavioral or quantitative claim. | +| [`b26add22f`](https://github.com/rocicorp/mono/pull/6204) | skip | - | Extends query-equivalence fuzzing through zero-cache and client materialization. | Omit as test-only; no runtime implementation changed. | +| [`89d57b460`](https://github.com/rocicorp/mono/pull/6210) | skip | - | Added replication-slot health and retained/safe WAL metrics. | Already shipped in 1.8 as patch-equivalent `f71c897c4` and covered by the 1.8 operational-metrics feature. | +| [`89c48bbc2`](https://github.com/rocicorp/mono/pull/6208) | skip | - | Added zero-cache worker startup duration metrics. | Already shipped in 1.8 as patch-equivalent `832580d65` and covered by the 1.8 operational-metrics feature. | +| [`7337ed18f`](https://github.com/rocicorp/mono/pull/6213) | feature | - | Adds opt-in npm `@rocicorp/zero@head` and GHCR `ghcr.io/rocicorp/zero:head` releases, plus immutable versions/tags containing the source SHA and date. Stable tags are unchanged. | Intentionally omit from Zero 1.9 notes after human review because this is a main-branch release channel, not a version-scoped runtime capability. Release-plan tests cover versions, branch/SHA validation, collisions, and refusal to create a git tag. | +| [`ca40512bf`](https://github.com/rocicorp/mono/pull/6218) | skip | - | Release README updates. | Already shipped in 1.8 as patch-equivalent `312a0f78f`; documentation-only and no additional 1.9 behavior. | +| [`6d84471c5`](https://github.com/rocicorp/mono/pull/6219) | fix | - | Excludes disconnected or not-yet-validated view-syncers from serving-lag metrics, preventing retained groups from producing false multi-hour spikes. | Include publicly, grouped with #6187. Human review classified the metric correction as non-breaking and requires operator-facing description of changed populations. Syncer tests cover eligible populations and cleanup; the change does not fix the underlying reason some disconnected view-syncers remain alive. | +| [`f9ff04d31`](https://github.com/rocicorp/mono/pull/6220) | fix | BREAKING | Resets PostgreSQL connections that carry no wire traffic across consecutive watchdog samples, allowing recovery from proxy-created half-open sockets. | Include with #6221 plus a Breaking Changes advisory. Default sampling is 120 seconds, giving an effective detection window of roughly two to four minutes. A legitimately silent statement can now be rejected; document `ZERO_PG_SOCKET_INACTIVITY_TIMEOUT` as the migration control and `0` as disabling the watchdog. | +| [`72f732cd4`](https://github.com/rocicorp/mono/pull/6221) | fix | BREAKING | Reimplements the inactivity watchdog so it survives postgres.js TLS socket upgrades. | Include with #6220 and the same migration advisory. Tests simulate TLS listener removal, activity, reset, warning logs, and disabling the watchdog. | +| [`e5b9c6f55`](https://github.com/rocicorp/mono/pull/6222) | skip | - | Accepts an optional nullable `newColumns` map in DDL events but does not emit or act on it. | Omit as reader-first rollout scaffolding for a future DDL optimization. Parsing tests cover present, null, and absent values. No migration, protocol bump, or 1.9 backfill behavior. | +| [`15fd7cdea`](https://github.com/rocicorp/mono/pull/6223) | skip | - | Exports `MutatorResult` for helpers that await client or server mutation results. | Already shipped in 1.8 as patch-equivalent `cdc02598f` and listed as a 1.8 feature. | +| [`d4f33d6a6`](https://github.com/rocicorp/mono/pull/6121) | fix | - | Makes compound cursor equality NULL-safe, preserves NULL groups in reverse walks, and propagates replica nullability so ordered pagination and window maintenance do not skip rows or hit `Bound should be set`. | Public candidate for the delta beyond already-shipped #6189. Query-builder, real-SQLite table-source, and lite-table tests cover tie-breaks, inclusive starts, reverse walks, and metadata. External author [@YevheniiKotyrlo](https://github.com/YevheniiKotyrlo), co-authored by Matt Wonlaw; GitHub profile shows no Rocicorp affiliation, so include thanks. | +| [`e374775a9`](https://github.com/rocicorp/mono/pull/6226) | skip | - | Avoids row remounts in the private zbugs application. | Omit as sample/private-app performance work; no Zero package changed. | +| [`7139287da`](https://github.com/rocicorp/mono/pull/6227) | skip | - | Migrates the private zbugs application to zero-virtual 0.6. | Omit as private-app dependency and layout work; no Zero API or runtime changed. | +| [`177d6c1f9`](https://github.com/rocicorp/mono/pull/6228) | fix | - | Disposes custom-query transformed-query caches when view-syncers stop, eliminating retained timers and cache maps for terminated client groups. | Public reliability candidate. Cache and transformer tests cover lazy/unrefed timers, idempotent destruction, and no timer resurrection. No API or TTL change. | +| [`ef892a123`](https://github.com/rocicorp/mono/pull/6224) | fix | - | Updates the optional bundled Litestream v5 executable from 0.5.11 to 0.5.14. Legacy Litestream remains the default. | Intentionally omit from public 1.9 notes after human review because it affects only the opt-in v5 executable. Upstream 0.5.12 includes restore WAL-gap detection, initial LTX-open retries, failed-restore cleanup, overwrite protection, and snapshot-setting fixes; 0.5.14 adds remote compaction reads, sustained S3 retries, replica-type handling, retention correction, and atomic SFTP writes. Keep v5 restore smoke coverage in the release handoff. | + +## Breaking-Change Review + +Human review identified one breaking operational change: the PostgreSQL inactivity watchdog can terminate a legitimately silent long-running statement. All `MAYBE` classifications are resolved. + +| Area | Finding | Required resolution | +| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| Public API and exports | No supported export is removed. The only new externally consumable interface is the additive `head` release channel. | No migration expected. The head channel is intentionally omitted from the version-scoped public note. | +| Configuration | #6182 adds default-on query-covering shadow analysis and `ZERO_ENABLE_QUERY_COVERING`. #6220 recognizes `ZERO_PG_SOCKET_INACTIVITY_TIMEOUT`. | Keep query covering internal. Document the PostgreSQL timeout control and `0` disable value as part of the breaking migration guidance. | +| Default behavior | PostgreSQL connections now reset after roughly two to four minutes without wire activity. Statement caches now retain at most 1,000 idle statements per cache. Load shedding may rebuild pipelines earlier. | Classify the watchdog as breaking. Human review accepted the statement-cache and load-shedding defaults as non-breaking. | +| Persisted data | No schema or replica migration. Correctly preserving `__proto__` can change the client schema hash for a previously broken schema and trigger normal resync behavior. | Narrow the claim; no special migration identified. | +| Protocol | Sync, change-stream, and DDL protocol constants are compatible. `newColumns` is optional and ignored when absent. | Resolved: compatible. | +| Dependencies | No npm runtime or peer dependency changes unique to 1.9. The release image changes optional Litestream v5 from 0.5.11 to 0.5.14. | Review upstream releases, completed above; retain v5 restore coverage in the smoke-test handoff. | +| Metrics and alerts | `total_lag`, `last_total_lag`, serving-lag populations, and retry reporting change semantics. | Update product docs and include alert-migration guidance; classified as non-breaking. | +| Deployment and rollback | The DDL reader accepts the optional future field without requiring writers to emit it. Head artifacts do not move `latest` or create git tags. | Resolved for protocol/rollback. Confirm release environments produce the intended head artifacts separately. | + +## Public Candidate Selection + +### Features + +- None selected. The head release channel (#6213) is intentionally omitted because it is not scoped to the 1.9 runtime. + +### Fixes + +- Ordered queries and window maintenance across NULL cursors (#6121), scoped to the additional tie-break, reverse-walk, and metadata behavior not already shipped through #6189. +- Preserving `__proto__` as user data in the specifically tested schema, mutation, and view paths (#6185). +- Same-hash query rehydration delivering changed rows instead of stale results (#6196). +- Bounded SQLite prepared-statement retention (#6202). +- Replication and serving-lag metric correctness (#6187 and #6219). +- Recovery from half-open PostgreSQL connections, including TLS (#6220 and #6221), with a breaking-change migration note. +- Custom-query cache/timer cleanup (#6228). +- Litestream v5 (#6224) is intentionally omitted because it affects only the opt-in v5 executable; retain restore smoke coverage in the release handoff. + +### Performance + +Deferred. + +Every non-skipped commit is represented or intentionally omitted above. #6206 remains deferred until release-quality end-to-end evidence is available. + +## Performance Evidence Plan + +Deferred. + +## Required Product Documentation + +- `contents/docs/otel.mdx`: correct `total_lag` and `last_total_lag`, add `lag_report_retries` and the same-hash rehydration counter, and describe serving-lag eligibility. +- `contents/docs/zero-cache-config.mdx`: update replication-lag-report retry behavior. Do not promote query-covering. +- `contents/docs/connecting-to-postgres.mdx`: document the socket inactivity behavior, supported override, and disable value. +- `contents/docs/release-notes/1.9.mdx`: create after audit review with performance deferred. +- `contents/docs/release-notes/index.mdx`: add Zero 1.9 first with a description matching frontmatter. +- Generated search and LLM artifacts: regenerate after product-doc edits. + +## Attribution + +- `d4f33d6a6` is authored by [@YevheniiKotyrlo](https://github.com/YevheniiKotyrlo) and co-authored by Matt Wonlaw. The author uses a GitHub noreply address and their public profile lists no company or Rocicorp affiliation. If the fix is included, append `(thanks [@YevheniiKotyrlo](https://github.com/YevheniiKotyrlo)!)`. +- `cb014857f` is authored by Erik Arvidsson but explicitly based on #6145 by external contributor [@tjenkinson](https://github.com/tjenkinson). Append `(thanks [@tjenkinson](https://github.com/tjenkinson)!)`. +- Other selected commits are authored by known Rocicorp contributors in the range. Recheck PR-level co-authors immediately before final publication. + +## Human Review Decisions + +- Classify the PostgreSQL inactivity watchdog as breaking and document `ZERO_PG_SOCKET_INACTIVITY_TIMEOUT`, including `0` to disable it. +- Defer #6206 benchmarks and publish no performance claim. +- Omit the head release channel from Zero 1.9 notes. +- Keep query-covering shadow telemetry internal and omit it publicly. +- Include the 1,000-entry statement-cache bound as a non-breaking reliability fix. +- Document the replication and serving-lag metric migration as a non-breaking operator-facing correction. +- Omit the optional Litestream v5 update from public notes, but retain its restore smoke test in the release handoff. + +Remaining blockers: + +1. Obtain a 1.9 canary for the smoke-test handoff. +2. Run the release-image Litestream v5 restore smoke test even though the dependency update is omitted publicly. + +## Audit Review Gate + +Human review is complete. Public drafting may proceed with performance deferred. + +## Validation + +- Audit coverage: PASS. All 39 raw-range commits have exactly one decision row; all 15 patch-equivalent 1.8 backports are recorded. +- Protocol compatibility: PASS. +- Placeholder links: PASS. No draft placeholders remain in the audit or release note. +- Formatting: PASS with `pnpm check-format` after formatting the generated search index. +- Types: PASS with `pnpm check-types`. +- Tests: PASS with `pnpm test` (73 tests). +- Production build: PASS with `pnpm build`; search and LLM artifacts were regenerated and Next.js built all 160 pages. +- Source lint: `pnpm exec oxlint --quiet` completed with 0 errors and 15 warnings. +- Type-aware lint: BLOCKED before file analysis. `pnpm lint` causes locked `oxlint@1.65.0` / `oxlint-tsgolint@0.8.6` to panic on unknown rule `no-useless-default-assignment`. This repository tool-version issue is recorded in the main repo's `PAPERCUTS.md`. +- Performance: Deferred by human review; no benchmark or public performance claim. diff --git a/assets/search-index.json b/assets/search-index.json index 0d4d7415..d1871063 100644 --- a/assets/search-index.json +++ b/assets/search-index.json @@ -14,7 +14,7 @@ "kind": "page" }, { - "id": "76-agents#options", + "id": "77-agents#options", "title": "Agent Support", "searchTitle": "Options", "sectionTitle": "Options", @@ -126,7 +126,7 @@ "kind": "page" }, { - "id": "77-auth#set-userid-on-client", + "id": "78-auth#set-userid-on-client", "title": "Authentication", "searchTitle": "Set userID on Client", "sectionTitle": "Set userID on Client", @@ -136,7 +136,7 @@ "kind": "section" }, { - "id": "78-auth#define-the-context-type", + "id": "79-auth#define-the-context-type", "title": "Authentication", "searchTitle": "Define the Context Type", "sectionTitle": "Define the Context Type", @@ -146,7 +146,7 @@ "kind": "section" }, { - "id": "79-auth#send-credentials", + "id": "80-auth#send-credentials", "title": "Authentication", "searchTitle": "Send Credentials", "sectionTitle": "Send Credentials", @@ -156,7 +156,7 @@ "kind": "section" }, { - "id": "80-auth#cookies", + "id": "81-auth#cookies", "title": "Authentication", "searchTitle": "Cookies", "sectionTitle": "Cookies", @@ -166,7 +166,7 @@ "kind": "section" }, { - "id": "81-auth#cookie-deployment", + "id": "82-auth#cookie-deployment", "title": "Authentication", "searchTitle": "Cookie Deployment", "sectionTitle": "Cookie Deployment", @@ -176,7 +176,7 @@ "kind": "section" }, { - "id": "82-auth#tokens", + "id": "83-auth#tokens", "title": "Authentication", "searchTitle": "Tokens", "sectionTitle": "Tokens", @@ -186,7 +186,7 @@ "kind": "section" }, { - "id": "83-auth#implement-api-endpoints", + "id": "84-auth#implement-api-endpoints", "title": "Authentication", "searchTitle": "Implement API Endpoints", "sectionTitle": "Implement API Endpoints", @@ -196,7 +196,7 @@ "kind": "section" }, { - "id": "84-auth#query", + "id": "85-auth#query", "title": "Authentication", "searchTitle": "Query", "sectionTitle": "Query", @@ -206,7 +206,7 @@ "kind": "section" }, { - "id": "85-auth#mutate", + "id": "86-auth#mutate", "title": "Authentication", "searchTitle": "Mutate", "sectionTitle": "Mutate", @@ -216,7 +216,7 @@ "kind": "section" }, { - "id": "86-auth#updating-tokens", + "id": "87-auth#updating-tokens", "title": "Authentication", "searchTitle": "Updating Tokens", "sectionTitle": "Updating Tokens", @@ -226,7 +226,7 @@ "kind": "section" }, { - "id": "87-auth#auth-failure-and-refresh", + "id": "88-auth#auth-failure-and-refresh", "title": "Authentication", "searchTitle": "Auth Failure and Refresh", "sectionTitle": "Auth Failure and Refresh", @@ -236,7 +236,7 @@ "kind": "section" }, { - "id": "88-auth#permission-patterns", + "id": "89-auth#permission-patterns", "title": "Authentication", "searchTitle": "Permission Patterns", "sectionTitle": "Permission Patterns", @@ -246,7 +246,7 @@ "kind": "section" }, { - "id": "89-auth#read-permissions", + "id": "90-auth#read-permissions", "title": "Authentication", "searchTitle": "Read Permissions", "sectionTitle": "Read Permissions", @@ -256,7 +256,7 @@ "kind": "section" }, { - "id": "90-auth#only-owned-rows", + "id": "91-auth#only-owned-rows", "title": "Authentication", "searchTitle": "Only Owned Rows", "sectionTitle": "Only Owned Rows", @@ -266,7 +266,7 @@ "kind": "section" }, { - "id": "91-auth#owned-or-shared-rows", + "id": "92-auth#owned-or-shared-rows", "title": "Authentication", "searchTitle": "Owned or Shared Rows", "sectionTitle": "Owned or Shared Rows", @@ -276,7 +276,7 @@ "kind": "section" }, { - "id": "92-auth#owned-rows-or-all-if-admin", + "id": "93-auth#owned-rows-or-all-if-admin", "title": "Authentication", "searchTitle": "Owned Rows or All if Admin", "sectionTitle": "Owned Rows or All if Admin", @@ -286,7 +286,7 @@ "kind": "section" }, { - "id": "93-auth#deny-by-returning-no-rows", + "id": "94-auth#deny-by-returning-no-rows", "title": "Authentication", "searchTitle": "Deny by Returning No Rows", "sectionTitle": "Deny by Returning No Rows", @@ -296,7 +296,7 @@ "kind": "section" }, { - "id": "94-auth#write-permissions", + "id": "95-auth#write-permissions", "title": "Authentication", "searchTitle": "Write Permissions", "sectionTitle": "Write Permissions", @@ -306,7 +306,7 @@ "kind": "section" }, { - "id": "95-auth#enforce-ownership", + "id": "96-auth#enforce-ownership", "title": "Authentication", "searchTitle": "Enforce Ownership", "sectionTitle": "Enforce Ownership", @@ -316,7 +316,7 @@ "kind": "section" }, { - "id": "96-auth#edit-owned-rows", + "id": "97-auth#edit-owned-rows", "title": "Authentication", "searchTitle": "Edit Owned Rows", "sectionTitle": "Edit Owned Rows", @@ -326,7 +326,7 @@ "kind": "section" }, { - "id": "97-auth#edit-owned-or-shared-rows", + "id": "98-auth#edit-owned-or-shared-rows", "title": "Authentication", "searchTitle": "Edit Owned or Shared Rows", "sectionTitle": "Edit Owned or Shared Rows", @@ -336,7 +336,7 @@ "kind": "section" }, { - "id": "98-auth#edit-owned-or-all-if-admin", + "id": "99-auth#edit-owned-or-all-if-admin", "title": "Authentication", "searchTitle": "Edit Owned or All if Admin", "sectionTitle": "Edit Owned or All if Admin", @@ -346,7 +346,7 @@ "kind": "section" }, { - "id": "99-auth#logging-out", + "id": "100-auth#logging-out", "title": "Authentication", "searchTitle": "Logging Out", "sectionTitle": "Logging Out", @@ -383,7 +383,7 @@ "kind": "page" }, { - "id": "100-community#ui-frameworks", + "id": "101-community#ui-frameworks", "title": "From the Community", "searchTitle": "UI Frameworks", "sectionTitle": "UI Frameworks", @@ -393,7 +393,7 @@ "kind": "section" }, { - "id": "101-community#miscellaneous", + "id": "102-community#miscellaneous", "title": "From the Community", "searchTitle": "Miscellaneous", "sectionTitle": "Miscellaneous", @@ -407,7 +407,7 @@ "title": "Connecting to Postgres", "searchTitle": "Connecting to Postgres", "url": "/docs/connecting-to-postgres", - "content": "In the future, Zero will work with many different backend databases. Today only Postgres is supported. Specifically, Zero requires Postgres v15.0 or higher, and support for logical replication. Here are some common Postgres options and what we know about their support level: Event Triggers Zero uses Postgres “Event Triggers” when possible to implement high-quality, efficient schema migration. Some hosted Postgres providers don't provide access to Event Triggers. Zero still works out of the box with these providers, but for correctness, any schema change triggers a full reset of all server-side and client-side state. For small databases (< 10GB) this can be OK, but for bigger databases you should either manually tell Zero about the schema change or choose a provider with event trigger support. Configuration WAL Level The Postgres wal_level config parameter has to be set to logical. You can check what level your pg has with this command: psql -c 'SHOW wal_level' If it doesn’t output logical then you need to change the wal level. To do this, run: psql -c \"ALTER SYSTEM SET wal_level = 'logical';\" Then restart Postgres. On most pg systems you can do this like so: data_dir=$(psql -t -A -c 'SHOW data_directory') pg_ctl -D \"$data_dir\" restart After your server restarts, show the wal_level again to ensure it has changed: psql -c 'SHOW wal_level' Bounding WAL Size For development databases, you can set a max_slot_wal_keep_size value in Postgres. This will help limit the amount of WAL kept around. This is a configuration parameter that bounds the amount of WAL kept around for replication slots, and invalidates the slots that are too far behind. Zero-cache will automatically detect if the replication slot has been invalidated and re-sync replicas from scratch. This configuration can cause problems like slot has been invalidated because it exceeded the maximum reserved size and is not recommended for production databases. Provider-Specific Notes PlanetScale for Postgres Roles zero-cache should connect using the default role that PlanetScale provides, because PlanetScale user-defined roles cannot create replication slots. Connection Limits Change max_connections to at least 100. The default is 25, which is too low for Zero in most configurations. Pooling Make sure to only use a direct connection for the ZERO_UPSTREAM_DB, and use pooled URLs for ZERO_CVR_DB, ZERO_CHANGE_DB, and your API (see Deployment). High Availability PlanetScale Postgres can fail over to a standby during maintenance or an outage. By default a logical replication slot does not survive promotion of a standby, so after a failover zero-cache would find its slot missing and re-sync every replica from scratch. To avoid this, first, run zero-cache with ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER=true so it creates failover-enabled slots. Then, run the script below to register Zero's replication slots with PlanetScale and enable the two cluster parameters failover needs: APP=\"\" # your ZERO_APP_ID — on Zero Cloud this is your instance ID ORG=\"\" # PlanetScale organization DB=\"\" # PlanetScale database BRANCH=\"main\" SHARD=\"0\" if [ -z \"$APP\" ] || [ -z \"$ORG\" ] || [ -z \"$DB\" ]; then echo \"Set APP, ORG, and DB first — nothing was sent.\" elif pscale api -X PATCH \"organizations/${ORG}/databases/${DB}/branches/${BRANCH}/changes\" --input=- >/dev/null </dev/null <\" OTEL_EXPORTER_OTLP_HEADERS=\"\" OTEL_RESOURCE_ATTRIBUTES=\"\" OTEL_NODE_RESOURCE_DETECTORS=\"env,host,os\" Grafana Cloud Walkthrough Here are instructions to setup Grafana Cloud, but the setup for other otel collectors should be similar. Sign up for Grafana Cloud (Free Tier) Click Connections > Add Connection in the left sidebar add-connection Search for \"OpenTelemetry\" and select it Click \"Quickstart\" quickstart Select \"JavaScript\" javascript Create a new token Copy the environment variables into your .env file or similar copy-env Start zero-cache Look for logs under \"Drilldown\" > \"Logs\" in left sidebar Distributed Tracing You can enable end-to-end trace correlation from your frontend through zero-cache to your API server. This allows you to see the full request flow in your tracing UI. To enable this, provide a getTraceparent callback when creating your Zero client: import {ZeroProvider} from '@rocicorp/zero/react' import {propagation, context} from '@opentelemetry/api' function getTraceparent() { const carrier: Record = {} propagation.inject(context.active(), carrier) return carrier.traceparent } return ( )import {ZeroProvider} from '@rocicorp/zero/solid' import {propagation, context} from '@opentelemetry/api' function getTraceparent() { const carrier: Record = {} propagation.inject(context.active(), carrier) return carrier.traceparent } return ( )import {Zero} from '@rocicorp/zero' import {propagation, context} from '@opentelemetry/api' const zero = new Zero({ // ... other options getTraceparent: () => { const carrier: Record = {} propagation.inject(context.active(), carrier) return carrier.traceparent } }) This callback is called before sending WebSocket messages that trigger API server calls (push, changeDesiredQueries, initConnection). The returned W3C traceparent header is forwarded through zero-cache to your API server, where it can be used to continue the trace. Metrics Reference view_syncer_lag and view_syncer_hydration require OpenTelemetry exponential histogram support. Prometheus users must enable native histograms. Use the existing serving_lag gauges if your backend does not support them. zero.server zero.replica zero.replication zero.sync zero.mutation", + "content": "The zero-cache service embeds the JavaScript OTLP Exporter and can send logs, traces, and metrics to any standard otel collector. To enable otel, set the following environment variables then run zero-cache as normal: OTEL_EXPORTER_OTLP_ENDPOINT=\"\" OTEL_EXPORTER_OTLP_HEADERS=\"\" OTEL_RESOURCE_ATTRIBUTES=\"\" OTEL_NODE_RESOURCE_DETECTORS=\"env,host,os\" Grafana Cloud Walkthrough Here are instructions to setup Grafana Cloud, but the setup for other otel collectors should be similar. Sign up for Grafana Cloud (Free Tier) Click Connections > Add Connection in the left sidebar add-connection Search for \"OpenTelemetry\" and select it Click \"Quickstart\" quickstart Select \"JavaScript\" javascript Create a new token Copy the environment variables into your .env file or similar copy-env Start zero-cache Look for logs under \"Drilldown\" > \"Logs\" in left sidebar Distributed Tracing You can enable end-to-end trace correlation from your frontend through zero-cache to your API server. This allows you to see the full request flow in your tracing UI. To enable this, provide a getTraceparent callback when creating your Zero client: import {ZeroProvider} from '@rocicorp/zero/react' import {propagation, context} from '@opentelemetry/api' function getTraceparent() { const carrier: Record = {} propagation.inject(context.active(), carrier) return carrier.traceparent } return ( )import {ZeroProvider} from '@rocicorp/zero/solid' import {propagation, context} from '@opentelemetry/api' function getTraceparent() { const carrier: Record = {} propagation.inject(context.active(), carrier) return carrier.traceparent } return ( )import {Zero} from '@rocicorp/zero' import {propagation, context} from '@opentelemetry/api' const zero = new Zero({ // ... other options getTraceparent: () => { const carrier: Record = {} propagation.inject(context.active(), carrier) return carrier.traceparent } }) This callback is called before sending WebSocket messages that trigger API server calls (push, changeDesiredQueries, initConnection). The returned W3C traceparent header is forwarded through zero-cache to your API server, where it can be used to continue the trace. Metrics Reference view_syncer_lag and view_syncer_hydration require OpenTelemetry exponential histogram support. Prometheus users must enable native histograms. Use the existing serving_lag gauges if your backend does not support them. zero.server zero.replica zero.replication total_lag and last_total_lag now report the same latest measured round trip and do not grow when reports stop arriving. Use lag_report_retries to detect a stalled or missing report stream. zero.sync Serving-lag metrics include only client groups with at least one connected client and a validated background connection context. Retained groups without an eligible connection do not contribute lag. zero.mutation", "headings": [ { "text": "Grafana Cloud Walkthrough", @@ -2328,7 +2342,7 @@ "kind": "page" }, { - "id": "226-otel#grafana-cloud-walkthrough", + "id": "228-otel#grafana-cloud-walkthrough", "title": "OpenTelemetry", "searchTitle": "Grafana Cloud Walkthrough", "sectionTitle": "Grafana Cloud Walkthrough", @@ -2338,7 +2352,7 @@ "kind": "section" }, { - "id": "227-otel#distributed-tracing", + "id": "229-otel#distributed-tracing", "title": "OpenTelemetry", "searchTitle": "Distributed Tracing", "sectionTitle": "Distributed Tracing", @@ -2348,17 +2362,17 @@ "kind": "section" }, { - "id": "228-otel#metrics-reference", + "id": "230-otel#metrics-reference", "title": "OpenTelemetry", "searchTitle": "Metrics Reference", "sectionTitle": "Metrics Reference", "sectionId": "metrics-reference", "url": "/docs/otel", - "content": "view_syncer_lag and view_syncer_hydration require OpenTelemetry exponential histogram support. Prometheus users must enable native histograms. Use the existing serving_lag gauges if your backend does not support them. zero.server zero.replica zero.replication zero.sync zero.mutation", + "content": "view_syncer_lag and view_syncer_hydration require OpenTelemetry exponential histogram support. Prometheus users must enable native histograms. Use the existing serving_lag gauges if your backend does not support them. zero.server zero.replica zero.replication total_lag and last_total_lag now report the same latest measured round trip and do not grow when reports stop arriving. Use lag_report_retries to detect a stalled or missing report stream. zero.sync Serving-lag metrics include only client groups with at least one connected client and a validated background connection context. Retained groups without an eligible connection do not contribute lag. zero.mutation", "kind": "section" }, { - "id": "229-otel#zeroserver", + "id": "231-otel#zeroserver", "title": "OpenTelemetry", "searchTitle": "zero.server", "sectionTitle": "zero.server", @@ -2368,7 +2382,7 @@ "kind": "section" }, { - "id": "230-otel#zeroreplica", + "id": "232-otel#zeroreplica", "title": "OpenTelemetry", "searchTitle": "zero.replica", "sectionTitle": "zero.replica", @@ -2378,27 +2392,27 @@ "kind": "section" }, { - "id": "231-otel#zeroreplication", + "id": "233-otel#zeroreplication", "title": "OpenTelemetry", "searchTitle": "zero.replication", "sectionTitle": "zero.replication", "sectionId": "zeroreplication", "url": "/docs/otel", - "content": "", + "content": "total_lag and last_total_lag now report the same latest measured round trip and do not grow when reports stop arriving. Use lag_report_retries to detect a stalled or missing report stream.", "kind": "section" }, { - "id": "232-otel#zerosync", + "id": "234-otel#zerosync", "title": "OpenTelemetry", "searchTitle": "zero.sync", "sectionTitle": "zero.sync", "sectionId": "zerosync", "url": "/docs/otel", - "content": "", + "content": "Serving-lag metrics include only client groups with at least one connected client and a validated background connection context. Retained groups without an eligible connection do not contribute lag.", "kind": "section" }, { - "id": "233-otel#zeromutation", + "id": "235-otel#zeromutation", "title": "OpenTelemetry", "searchTitle": "zero.mutation", "sectionTitle": "zero.mutation", @@ -2458,7 +2472,7 @@ "kind": "page" }, { - "id": "234-postgres-support#object-names", + "id": "236-postgres-support#object-names", "title": "Supported Postgres Features", "searchTitle": "Object Names", "sectionTitle": "Object Names", @@ -2468,7 +2482,7 @@ "kind": "section" }, { - "id": "235-postgres-support#object-types", + "id": "237-postgres-support#object-types", "title": "Supported Postgres Features", "searchTitle": "Object Types", "sectionTitle": "Object Types", @@ -2478,7 +2492,7 @@ "kind": "section" }, { - "id": "236-postgres-support#column-types", + "id": "238-postgres-support#column-types", "title": "Supported Postgres Features", "searchTitle": "Column Types", "sectionTitle": "Column Types", @@ -2488,7 +2502,7 @@ "kind": "section" }, { - "id": "237-postgres-support#column-defaults", + "id": "239-postgres-support#column-defaults", "title": "Supported Postgres Features", "searchTitle": "Column Defaults", "sectionTitle": "Column Defaults", @@ -2498,7 +2512,7 @@ "kind": "section" }, { - "id": "238-postgres-support#ids", + "id": "240-postgres-support#ids", "title": "Supported Postgres Features", "searchTitle": "IDs", "sectionTitle": "IDs", @@ -2508,7 +2522,7 @@ "kind": "section" }, { - "id": "239-postgres-support#primary-keys", + "id": "241-postgres-support#primary-keys", "title": "Supported Postgres Features", "searchTitle": "Primary Keys", "sectionTitle": "Primary Keys", @@ -2518,7 +2532,7 @@ "kind": "section" }, { - "id": "240-postgres-support#limiting-replication", + "id": "242-postgres-support#limiting-replication", "title": "Supported Postgres Features", "searchTitle": "Limiting Replication", "sectionTitle": "Limiting Replication", @@ -2528,7 +2542,7 @@ "kind": "section" }, { - "id": "241-postgres-support#zero-cache-replication", + "id": "243-postgres-support#zero-cache-replication", "title": "Supported Postgres Features", "searchTitle": "zero-cache replication", "sectionTitle": "zero-cache replication", @@ -2538,7 +2552,7 @@ "kind": "section" }, { - "id": "242-postgres-support#browser-client-replication", + "id": "244-postgres-support#browser-client-replication", "title": "Supported Postgres Features", "searchTitle": "Browser client replication", "sectionTitle": "Browser client replication", @@ -2548,7 +2562,7 @@ "kind": "section" }, { - "id": "243-postgres-support#schema-changes", + "id": "245-postgres-support#schema-changes", "title": "Supported Postgres Features", "searchTitle": "Schema changes", "sectionTitle": "Schema changes", @@ -2584,7 +2598,7 @@ "kind": "page" }, { - "id": "244-previews#overview", + "id": "246-previews#overview", "title": "Previews", "searchTitle": "Overview", "sectionTitle": "Overview", @@ -2594,7 +2608,7 @@ "kind": "section" }, { - "id": "245-previews#configure-allowed-endpoint-patterns", + "id": "247-previews#configure-allowed-endpoint-patterns", "title": "Previews", "searchTitle": "Configure Allowed Endpoint Patterns", "sectionTitle": "Configure Allowed Endpoint Patterns", @@ -2604,7 +2618,7 @@ "kind": "section" }, { - "id": "246-previews#choose-endpoint-urls-in-the-client", + "id": "248-previews#choose-endpoint-urls-in-the-client", "title": "Previews", "searchTitle": "Choose Endpoint URLs in the Client", "sectionTitle": "Choose Endpoint URLs in the Client", @@ -2614,7 +2628,7 @@ "kind": "section" }, { - "id": "247-previews#schema-changes-in-previews", + "id": "249-previews#schema-changes-in-previews", "title": "Previews", "searchTitle": "Schema Changes in Previews", "sectionTitle": "Schema Changes in Previews", @@ -2758,7 +2772,7 @@ "kind": "page" }, { - "id": "248-queries#architecture", + "id": "250-queries#architecture", "title": "Queries", "searchTitle": "Architecture", "sectionTitle": "Architecture", @@ -2768,7 +2782,7 @@ "kind": "section" }, { - "id": "249-queries#life-of-a-query", + "id": "251-queries#life-of-a-query", "title": "Queries", "searchTitle": "Life of a Query", "sectionTitle": "Life of a Query", @@ -2778,7 +2792,7 @@ "kind": "section" }, { - "id": "250-queries#defining-queries", + "id": "252-queries#defining-queries", "title": "Queries", "searchTitle": "Defining Queries", "sectionTitle": "Defining Queries", @@ -2788,7 +2802,7 @@ "kind": "section" }, { - "id": "251-queries#basics", + "id": "253-queries#basics", "title": "Queries", "searchTitle": "Basics", "sectionTitle": "Basics", @@ -2798,7 +2812,7 @@ "kind": "section" }, { - "id": "252-queries#arguments", + "id": "254-queries#arguments", "title": "Queries", "searchTitle": "Arguments", "sectionTitle": "Arguments", @@ -2808,7 +2822,7 @@ "kind": "section" }, { - "id": "253-queries#query-registries", + "id": "255-queries#query-registries", "title": "Queries", "searchTitle": "Query Registries", "sectionTitle": "Query Registries", @@ -2818,7 +2832,7 @@ "kind": "section" }, { - "id": "254-queries#query-names", + "id": "256-queries#query-names", "title": "Queries", "searchTitle": "Query Names", "sectionTitle": "Query Names", @@ -2828,7 +2842,7 @@ "kind": "section" }, { - "id": "255-queries#context", + "id": "257-queries#context", "title": "Queries", "searchTitle": "Context", "sectionTitle": "Context", @@ -2838,7 +2852,7 @@ "kind": "section" }, { - "id": "256-queries#queriests", + "id": "258-queries#queriests", "title": "Queries", "searchTitle": "queries.ts", "sectionTitle": "queries.ts", @@ -2848,7 +2862,7 @@ "kind": "section" }, { - "id": "257-queries#server-setup", + "id": "259-queries#server-setup", "title": "Queries", "searchTitle": "Server Setup", "sectionTitle": "Server Setup", @@ -2858,7 +2872,7 @@ "kind": "section" }, { - "id": "258-queries#registering-the-endpoint", + "id": "260-queries#registering-the-endpoint", "title": "Queries", "searchTitle": "Registering the Endpoint", "sectionTitle": "Registering the Endpoint", @@ -2868,7 +2882,7 @@ "kind": "section" }, { - "id": "259-queries#implementing-the-endpoint", + "id": "261-queries#implementing-the-endpoint", "title": "Queries", "searchTitle": "Implementing the Endpoint", "sectionTitle": "Implementing the Endpoint", @@ -2878,7 +2892,7 @@ "kind": "section" }, { - "id": "260-queries#custom-query-url", + "id": "262-queries#custom-query-url", "title": "Queries", "searchTitle": "Custom Query URL", "sectionTitle": "Custom Query URL", @@ -2888,7 +2902,7 @@ "kind": "section" }, { - "id": "261-queries#url-patterns", + "id": "263-queries#url-patterns", "title": "Queries", "searchTitle": "URL Patterns", "sectionTitle": "URL Patterns", @@ -2898,7 +2912,7 @@ "kind": "section" }, { - "id": "262-queries#running-queries", + "id": "264-queries#running-queries", "title": "Queries", "searchTitle": "Running Queries", "sectionTitle": "Running Queries", @@ -2908,7 +2922,7 @@ "kind": "section" }, { - "id": "263-queries#reactively", + "id": "265-queries#reactively", "title": "Queries", "searchTitle": "Reactively", "sectionTitle": "Reactively", @@ -2918,7 +2932,7 @@ "kind": "section" }, { - "id": "264-queries#conditionally", + "id": "266-queries#conditionally", "title": "Queries", "searchTitle": "Conditionally", "sectionTitle": "Conditionally", @@ -2928,7 +2942,7 @@ "kind": "section" }, { - "id": "265-queries#once", + "id": "267-queries#once", "title": "Queries", "searchTitle": "Once", "sectionTitle": "Once", @@ -2938,7 +2952,7 @@ "kind": "section" }, { - "id": "266-queries#for-preloading", + "id": "268-queries#for-preloading", "title": "Queries", "searchTitle": "For Preloading", "sectionTitle": "For Preloading", @@ -2948,7 +2962,7 @@ "kind": "section" }, { - "id": "267-queries#missing-data", + "id": "269-queries#missing-data", "title": "Queries", "searchTitle": "Missing Data", "sectionTitle": "Missing Data", @@ -2958,7 +2972,7 @@ "kind": "section" }, { - "id": "268-queries#partial-data", + "id": "270-queries#partial-data", "title": "Queries", "searchTitle": "Partial Data", "sectionTitle": "Partial Data", @@ -2968,7 +2982,7 @@ "kind": "section" }, { - "id": "269-queries#handling-errors", + "id": "271-queries#handling-errors", "title": "Queries", "searchTitle": "Handling Errors", "sectionTitle": "Handling Errors", @@ -2978,7 +2992,7 @@ "kind": "section" }, { - "id": "270-queries#granular-updates", + "id": "272-queries#granular-updates", "title": "Queries", "searchTitle": "Granular Updates", "sectionTitle": "Granular Updates", @@ -2988,7 +3002,7 @@ "kind": "section" }, { - "id": "271-queries#query-caching", + "id": "273-queries#query-caching", "title": "Queries", "searchTitle": "Query Caching", "sectionTitle": "Query Caching", @@ -2998,7 +3012,7 @@ "kind": "section" }, { - "id": "272-queries#ttls", + "id": "274-queries#ttls", "title": "Queries", "searchTitle": "TTLs", "sectionTitle": "TTLs", @@ -3008,7 +3022,7 @@ "kind": "section" }, { - "id": "273-queries#ttl-defaults", + "id": "275-queries#ttl-defaults", "title": "Queries", "searchTitle": "TTL Defaults", "sectionTitle": "TTL Defaults", @@ -3018,7 +3032,7 @@ "kind": "section" }, { - "id": "274-queries#setting-different-ttls", + "id": "276-queries#setting-different-ttls", "title": "Queries", "searchTitle": "Setting Different TTLs", "sectionTitle": "Setting Different TTLs", @@ -3028,7 +3042,7 @@ "kind": "section" }, { - "id": "275-queries#why-zero-ttls-are-short", + "id": "277-queries#why-zero-ttls-are-short", "title": "Queries", "searchTitle": "Why Zero TTLs are Short", "sectionTitle": "Why Zero TTLs are Short", @@ -3038,7 +3052,7 @@ "kind": "section" }, { - "id": "276-queries#local-only-queries", + "id": "278-queries#local-only-queries", "title": "Queries", "searchTitle": "Local-Only Queries", "sectionTitle": "Local-Only Queries", @@ -3048,7 +3062,7 @@ "kind": "section" }, { - "id": "277-queries#custom-server-implementation", + "id": "279-queries#custom-server-implementation", "title": "Queries", "searchTitle": "Custom Server Implementation", "sectionTitle": "Custom Server Implementation", @@ -3058,7 +3072,7 @@ "kind": "section" }, { - "id": "278-queries#consistency", + "id": "280-queries#consistency", "title": "Queries", "searchTitle": "Consistency", "sectionTitle": "Consistency", @@ -3090,7 +3104,7 @@ "kind": "page" }, { - "id": "279-quickstart#hello-zero-solid", + "id": "281-quickstart#hello-zero-solid", "title": "Quickstart", "searchTitle": "hello-zero-solid", "sectionTitle": "hello-zero-solid", @@ -3100,7 +3114,7 @@ "kind": "section" }, { - "id": "280-quickstart#hello-zero-cf", + "id": "282-quickstart#hello-zero-cf", "title": "Quickstart", "searchTitle": "hello-zero-cf", "sectionTitle": "hello-zero-cf", @@ -3110,7 +3124,7 @@ "kind": "section" }, { - "id": "281-quickstart#hello-zero", + "id": "283-quickstart#hello-zero", "title": "Quickstart", "searchTitle": "hello-zero", "sectionTitle": "hello-zero", @@ -3155,7 +3169,7 @@ "kind": "page" }, { - "id": "282-react#setup", + "id": "284-react#setup", "title": "React", "searchTitle": "Setup", "sectionTitle": "Setup", @@ -3165,7 +3179,7 @@ "kind": "section" }, { - "id": "283-react#usage", + "id": "285-react#usage", "title": "React", "searchTitle": "Usage", "sectionTitle": "Usage", @@ -3175,7 +3189,7 @@ "kind": "section" }, { - "id": "284-react#suspense", + "id": "286-react#suspense", "title": "React", "searchTitle": "Suspense", "sectionTitle": "Suspense", @@ -3185,7 +3199,7 @@ "kind": "section" }, { - "id": "285-react#examples", + "id": "287-react#examples", "title": "React", "searchTitle": "Examples", "sectionTitle": "Examples", @@ -3217,7 +3231,7 @@ "kind": "page" }, { - "id": "286-release-notes/0.1#breaking-changes", + "id": "288-release-notes/0.1#breaking-changes", "title": "Zero 0.1", "searchTitle": "Breaking changes", "sectionTitle": "Breaking changes", @@ -3227,7 +3241,7 @@ "kind": "section" }, { - "id": "287-release-notes/0.1#features", + "id": "289-release-notes/0.1#features", "title": "Zero 0.1", "searchTitle": "Features", "sectionTitle": "Features", @@ -3237,7 +3251,7 @@ "kind": "section" }, { - "id": "288-release-notes/0.1#source-tree-fixes", + "id": "290-release-notes/0.1#source-tree-fixes", "title": "Zero 0.1", "searchTitle": "Source tree fixes", "sectionTitle": "Source tree fixes", @@ -3273,7 +3287,7 @@ "kind": "page" }, { - "id": "289-release-notes/0.10#install", + "id": "291-release-notes/0.10#install", "title": "Zero 0.10", "searchTitle": "Install", "sectionTitle": "Install", @@ -3283,7 +3297,7 @@ "kind": "section" }, { - "id": "290-release-notes/0.10#features", + "id": "292-release-notes/0.10#features", "title": "Zero 0.10", "searchTitle": "Features", "sectionTitle": "Features", @@ -3293,7 +3307,7 @@ "kind": "section" }, { - "id": "291-release-notes/0.10#fixes", + "id": "293-release-notes/0.10#fixes", "title": "Zero 0.10", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -3303,7 +3317,7 @@ "kind": "section" }, { - "id": "292-release-notes/0.10#breaking-changes", + "id": "294-release-notes/0.10#breaking-changes", "title": "Zero 0.10", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -3339,7 +3353,7 @@ "kind": "page" }, { - "id": "293-release-notes/0.11#install", + "id": "295-release-notes/0.11#install", "title": "Zero 0.11", "searchTitle": "Install", "sectionTitle": "Install", @@ -3349,7 +3363,7 @@ "kind": "section" }, { - "id": "294-release-notes/0.11#features", + "id": "296-release-notes/0.11#features", "title": "Zero 0.11", "searchTitle": "Features", "sectionTitle": "Features", @@ -3359,7 +3373,7 @@ "kind": "section" }, { - "id": "295-release-notes/0.11#fixes", + "id": "297-release-notes/0.11#fixes", "title": "Zero 0.11", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -3369,7 +3383,7 @@ "kind": "section" }, { - "id": "296-release-notes/0.11#breaking-changes", + "id": "298-release-notes/0.11#breaking-changes", "title": "Zero 0.11", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -3405,7 +3419,7 @@ "kind": "page" }, { - "id": "297-release-notes/0.12#install", + "id": "299-release-notes/0.12#install", "title": "Zero 0.12", "searchTitle": "Install", "sectionTitle": "Install", @@ -3415,7 +3429,7 @@ "kind": "section" }, { - "id": "298-release-notes/0.12#features", + "id": "300-release-notes/0.12#features", "title": "Zero 0.12", "searchTitle": "Features", "sectionTitle": "Features", @@ -3425,7 +3439,7 @@ "kind": "section" }, { - "id": "299-release-notes/0.12#fixes", + "id": "301-release-notes/0.12#fixes", "title": "Zero 0.12", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -3435,7 +3449,7 @@ "kind": "section" }, { - "id": "300-release-notes/0.12#breaking-changes", + "id": "302-release-notes/0.12#breaking-changes", "title": "Zero 0.12", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -3471,7 +3485,7 @@ "kind": "page" }, { - "id": "301-release-notes/0.13#install", + "id": "303-release-notes/0.13#install", "title": "Zero 0.13", "searchTitle": "Install", "sectionTitle": "Install", @@ -3481,7 +3495,7 @@ "kind": "section" }, { - "id": "302-release-notes/0.13#features", + "id": "304-release-notes/0.13#features", "title": "Zero 0.13", "searchTitle": "Features", "sectionTitle": "Features", @@ -3491,7 +3505,7 @@ "kind": "section" }, { - "id": "303-release-notes/0.13#fixes", + "id": "305-release-notes/0.13#fixes", "title": "Zero 0.13", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -3501,7 +3515,7 @@ "kind": "section" }, { - "id": "304-release-notes/0.13#breaking-changes", + "id": "306-release-notes/0.13#breaking-changes", "title": "Zero 0.13", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -3537,7 +3551,7 @@ "kind": "page" }, { - "id": "305-release-notes/0.14#install", + "id": "307-release-notes/0.14#install", "title": "Zero 0.14", "searchTitle": "Install", "sectionTitle": "Install", @@ -3547,7 +3561,7 @@ "kind": "section" }, { - "id": "306-release-notes/0.14#features", + "id": "308-release-notes/0.14#features", "title": "Zero 0.14", "searchTitle": "Features", "sectionTitle": "Features", @@ -3557,7 +3571,7 @@ "kind": "section" }, { - "id": "307-release-notes/0.14#fixes", + "id": "309-release-notes/0.14#fixes", "title": "Zero 0.14", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -3567,7 +3581,7 @@ "kind": "section" }, { - "id": "308-release-notes/0.14#breaking-changes", + "id": "310-release-notes/0.14#breaking-changes", "title": "Zero 0.14", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -3607,7 +3621,7 @@ "kind": "page" }, { - "id": "309-release-notes/0.15#install", + "id": "311-release-notes/0.15#install", "title": "Zero 0.15", "searchTitle": "Install", "sectionTitle": "Install", @@ -3617,7 +3631,7 @@ "kind": "section" }, { - "id": "310-release-notes/0.15#upgrade-guide", + "id": "312-release-notes/0.15#upgrade-guide", "title": "Zero 0.15", "searchTitle": "Upgrade Guide", "sectionTitle": "Upgrade Guide", @@ -3627,7 +3641,7 @@ "kind": "section" }, { - "id": "311-release-notes/0.15#features", + "id": "313-release-notes/0.15#features", "title": "Zero 0.15", "searchTitle": "Features", "sectionTitle": "Features", @@ -3637,7 +3651,7 @@ "kind": "section" }, { - "id": "312-release-notes/0.15#fixes", + "id": "314-release-notes/0.15#fixes", "title": "Zero 0.15", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -3647,7 +3661,7 @@ "kind": "section" }, { - "id": "313-release-notes/0.15#breaking-changes", + "id": "315-release-notes/0.15#breaking-changes", "title": "Zero 0.15", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -3687,7 +3701,7 @@ "kind": "page" }, { - "id": "314-release-notes/0.16#install", + "id": "316-release-notes/0.16#install", "title": "Zero 0.16", "searchTitle": "Install", "sectionTitle": "Install", @@ -3697,7 +3711,7 @@ "kind": "section" }, { - "id": "315-release-notes/0.16#upgrading", + "id": "317-release-notes/0.16#upgrading", "title": "Zero 0.16", "searchTitle": "Upgrading", "sectionTitle": "Upgrading", @@ -3707,7 +3721,7 @@ "kind": "section" }, { - "id": "316-release-notes/0.16#features", + "id": "318-release-notes/0.16#features", "title": "Zero 0.16", "searchTitle": "Features", "sectionTitle": "Features", @@ -3717,7 +3731,7 @@ "kind": "section" }, { - "id": "317-release-notes/0.16#fixes", + "id": "319-release-notes/0.16#fixes", "title": "Zero 0.16", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -3727,7 +3741,7 @@ "kind": "section" }, { - "id": "318-release-notes/0.16#breaking-changes", + "id": "320-release-notes/0.16#breaking-changes", "title": "Zero 0.16", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -3767,7 +3781,7 @@ "kind": "page" }, { - "id": "319-release-notes/0.17#install", + "id": "321-release-notes/0.17#install", "title": "Zero 0.17", "searchTitle": "Install", "sectionTitle": "Install", @@ -3777,7 +3791,7 @@ "kind": "section" }, { - "id": "320-release-notes/0.17#upgrading", + "id": "322-release-notes/0.17#upgrading", "title": "Zero 0.17", "searchTitle": "Upgrading", "sectionTitle": "Upgrading", @@ -3787,7 +3801,7 @@ "kind": "section" }, { - "id": "321-release-notes/0.17#features", + "id": "323-release-notes/0.17#features", "title": "Zero 0.17", "searchTitle": "Features", "sectionTitle": "Features", @@ -3797,7 +3811,7 @@ "kind": "section" }, { - "id": "322-release-notes/0.17#fixes", + "id": "324-release-notes/0.17#fixes", "title": "Zero 0.17", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -3807,7 +3821,7 @@ "kind": "section" }, { - "id": "323-release-notes/0.17#breaking-changes", + "id": "325-release-notes/0.17#breaking-changes", "title": "Zero 0.17", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -3847,7 +3861,7 @@ "kind": "page" }, { - "id": "324-release-notes/0.18#install", + "id": "326-release-notes/0.18#install", "title": "Zero 0.18", "searchTitle": "Install", "sectionTitle": "Install", @@ -3857,7 +3871,7 @@ "kind": "section" }, { - "id": "325-release-notes/0.18#upgrading", + "id": "327-release-notes/0.18#upgrading", "title": "Zero 0.18", "searchTitle": "Upgrading", "sectionTitle": "Upgrading", @@ -3867,7 +3881,7 @@ "kind": "section" }, { - "id": "326-release-notes/0.18#features", + "id": "328-release-notes/0.18#features", "title": "Zero 0.18", "searchTitle": "Features", "sectionTitle": "Features", @@ -3877,7 +3891,7 @@ "kind": "section" }, { - "id": "327-release-notes/0.18#fixes", + "id": "329-release-notes/0.18#fixes", "title": "Zero 0.18", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -3887,7 +3901,7 @@ "kind": "section" }, { - "id": "328-release-notes/0.18#breaking-changes", + "id": "330-release-notes/0.18#breaking-changes", "title": "Zero 0.18", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -3927,7 +3941,7 @@ "kind": "page" }, { - "id": "329-release-notes/0.19#install", + "id": "331-release-notes/0.19#install", "title": "Zero 0.19", "searchTitle": "Install", "sectionTitle": "Install", @@ -3937,7 +3951,7 @@ "kind": "section" }, { - "id": "330-release-notes/0.19#upgrading", + "id": "332-release-notes/0.19#upgrading", "title": "Zero 0.19", "searchTitle": "Upgrading", "sectionTitle": "Upgrading", @@ -3947,7 +3961,7 @@ "kind": "section" }, { - "id": "331-release-notes/0.19#features", + "id": "333-release-notes/0.19#features", "title": "Zero 0.19", "searchTitle": "Features", "sectionTitle": "Features", @@ -3957,7 +3971,7 @@ "kind": "section" }, { - "id": "332-release-notes/0.19#fixes", + "id": "334-release-notes/0.19#fixes", "title": "Zero 0.19", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -3967,7 +3981,7 @@ "kind": "section" }, { - "id": "333-release-notes/0.19#breaking-changes", + "id": "335-release-notes/0.19#breaking-changes", "title": "Zero 0.19", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -4011,7 +4025,7 @@ "kind": "page" }, { - "id": "334-release-notes/0.2#breaking-changes", + "id": "336-release-notes/0.2#breaking-changes", "title": "Zero 0.2", "searchTitle": "Breaking changes", "sectionTitle": "Breaking changes", @@ -4021,7 +4035,7 @@ "kind": "section" }, { - "id": "335-release-notes/0.2#features", + "id": "337-release-notes/0.2#features", "title": "Zero 0.2", "searchTitle": "Features", "sectionTitle": "Features", @@ -4031,7 +4045,7 @@ "kind": "section" }, { - "id": "336-release-notes/0.2#fixes", + "id": "338-release-notes/0.2#fixes", "title": "Zero 0.2", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -4041,7 +4055,7 @@ "kind": "section" }, { - "id": "337-release-notes/0.2#docs", + "id": "339-release-notes/0.2#docs", "title": "Zero 0.2", "searchTitle": "Docs", "sectionTitle": "Docs", @@ -4051,7 +4065,7 @@ "kind": "section" }, { - "id": "338-release-notes/0.2#source-tree-fixes", + "id": "340-release-notes/0.2#source-tree-fixes", "title": "Zero 0.2", "searchTitle": "Source tree fixes", "sectionTitle": "Source tree fixes", @@ -4061,7 +4075,7 @@ "kind": "section" }, { - "id": "339-release-notes/0.2#zbugs", + "id": "341-release-notes/0.2#zbugs", "title": "Zero 0.2", "searchTitle": "zbugs", "sectionTitle": "zbugs", @@ -4101,7 +4115,7 @@ "kind": "page" }, { - "id": "340-release-notes/0.20#install", + "id": "342-release-notes/0.20#install", "title": "Zero 0.20", "searchTitle": "Install", "sectionTitle": "Install", @@ -4111,7 +4125,7 @@ "kind": "section" }, { - "id": "341-release-notes/0.20#upgrading", + "id": "343-release-notes/0.20#upgrading", "title": "Zero 0.20", "searchTitle": "Upgrading", "sectionTitle": "Upgrading", @@ -4121,7 +4135,7 @@ "kind": "section" }, { - "id": "342-release-notes/0.20#features", + "id": "344-release-notes/0.20#features", "title": "Zero 0.20", "searchTitle": "Features", "sectionTitle": "Features", @@ -4131,7 +4145,7 @@ "kind": "section" }, { - "id": "343-release-notes/0.20#fixes", + "id": "345-release-notes/0.20#fixes", "title": "Zero 0.20", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -4141,7 +4155,7 @@ "kind": "section" }, { - "id": "344-release-notes/0.20#breaking-changes", + "id": "346-release-notes/0.20#breaking-changes", "title": "Zero 0.20", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -4181,7 +4195,7 @@ "kind": "page" }, { - "id": "345-release-notes/0.21#install", + "id": "347-release-notes/0.21#install", "title": "Zero 0.21", "searchTitle": "Install", "sectionTitle": "Install", @@ -4191,7 +4205,7 @@ "kind": "section" }, { - "id": "346-release-notes/0.21#upgrading", + "id": "348-release-notes/0.21#upgrading", "title": "Zero 0.21", "searchTitle": "Upgrading", "sectionTitle": "Upgrading", @@ -4201,7 +4215,7 @@ "kind": "section" }, { - "id": "347-release-notes/0.21#features", + "id": "349-release-notes/0.21#features", "title": "Zero 0.21", "searchTitle": "Features", "sectionTitle": "Features", @@ -4211,7 +4225,7 @@ "kind": "section" }, { - "id": "348-release-notes/0.21#fixes", + "id": "350-release-notes/0.21#fixes", "title": "Zero 0.21", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -4221,7 +4235,7 @@ "kind": "section" }, { - "id": "349-release-notes/0.21#breaking-changes", + "id": "351-release-notes/0.21#breaking-changes", "title": "Zero 0.21", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -4273,7 +4287,7 @@ "kind": "page" }, { - "id": "350-release-notes/0.22#install", + "id": "352-release-notes/0.22#install", "title": "Zero 0.22", "searchTitle": "Install", "sectionTitle": "Install", @@ -4283,7 +4297,7 @@ "kind": "section" }, { - "id": "351-release-notes/0.22#upgrading", + "id": "353-release-notes/0.22#upgrading", "title": "Zero 0.22", "searchTitle": "Upgrading", "sectionTitle": "Upgrading", @@ -4293,7 +4307,7 @@ "kind": "section" }, { - "id": "352-release-notes/0.22#how-ttls-used-to-work", + "id": "354-release-notes/0.22#how-ttls-used-to-work", "title": "Zero 0.22", "searchTitle": "How TTLs Used to Work", "sectionTitle": "How TTLs Used to Work", @@ -4303,7 +4317,7 @@ "kind": "section" }, { - "id": "353-release-notes/0.22#how-ttls-work-now", + "id": "355-release-notes/0.22#how-ttls-work-now", "title": "Zero 0.22", "searchTitle": "How TTLs Work Now", "sectionTitle": "How TTLs Work Now", @@ -4313,7 +4327,7 @@ "kind": "section" }, { - "id": "354-release-notes/0.22#using-new-ttls", + "id": "356-release-notes/0.22#using-new-ttls", "title": "Zero 0.22", "searchTitle": "Using New TTLs", "sectionTitle": "Using New TTLs", @@ -4323,7 +4337,7 @@ "kind": "section" }, { - "id": "355-release-notes/0.22#features", + "id": "357-release-notes/0.22#features", "title": "Zero 0.22", "searchTitle": "Features", "sectionTitle": "Features", @@ -4333,7 +4347,7 @@ "kind": "section" }, { - "id": "356-release-notes/0.22#fixes", + "id": "358-release-notes/0.22#fixes", "title": "Zero 0.22", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -4343,7 +4357,7 @@ "kind": "section" }, { - "id": "357-release-notes/0.22#breaking-changes", + "id": "359-release-notes/0.22#breaking-changes", "title": "Zero 0.22", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -4387,7 +4401,7 @@ "kind": "page" }, { - "id": "358-release-notes/0.23#install", + "id": "360-release-notes/0.23#install", "title": "Zero 0.23", "searchTitle": "Install", "sectionTitle": "Install", @@ -4397,7 +4411,7 @@ "kind": "section" }, { - "id": "359-release-notes/0.23#upgrading", + "id": "361-release-notes/0.23#upgrading", "title": "Zero 0.23", "searchTitle": "Upgrading", "sectionTitle": "Upgrading", @@ -4407,7 +4421,7 @@ "kind": "section" }, { - "id": "360-release-notes/0.23#features", + "id": "362-release-notes/0.23#features", "title": "Zero 0.23", "searchTitle": "Features", "sectionTitle": "Features", @@ -4417,7 +4431,7 @@ "kind": "section" }, { - "id": "361-release-notes/0.23#fixes", + "id": "363-release-notes/0.23#fixes", "title": "Zero 0.23", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -4427,7 +4441,7 @@ "kind": "section" }, { - "id": "362-release-notes/0.23#zbugs", + "id": "364-release-notes/0.23#zbugs", "title": "Zero 0.23", "searchTitle": "zbugs", "sectionTitle": "zbugs", @@ -4437,7 +4451,7 @@ "kind": "section" }, { - "id": "363-release-notes/0.23#breaking-changes", + "id": "365-release-notes/0.23#breaking-changes", "title": "Zero 0.23", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -4477,7 +4491,7 @@ "kind": "page" }, { - "id": "364-release-notes/0.24#installation", + "id": "366-release-notes/0.24#installation", "title": "Zero 0.24", "searchTitle": "Installation", "sectionTitle": "Installation", @@ -4487,7 +4501,7 @@ "kind": "section" }, { - "id": "365-release-notes/0.24#features", + "id": "367-release-notes/0.24#features", "title": "Zero 0.24", "searchTitle": "Features", "sectionTitle": "Features", @@ -4497,7 +4511,7 @@ "kind": "section" }, { - "id": "366-release-notes/0.24#fixes", + "id": "368-release-notes/0.24#fixes", "title": "Zero 0.24", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -4507,7 +4521,7 @@ "kind": "section" }, { - "id": "367-release-notes/0.24#breaking-changes", + "id": "369-release-notes/0.24#breaking-changes", "title": "Zero 0.24", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -4517,7 +4531,7 @@ "kind": "section" }, { - "id": "368-release-notes/0.24#example-upgrades", + "id": "370-release-notes/0.24#example-upgrades", "title": "Zero 0.24", "searchTitle": "Example Upgrades", "sectionTitle": "Example Upgrades", @@ -4565,7 +4579,7 @@ "kind": "page" }, { - "id": "369-release-notes/0.25#installation", + "id": "371-release-notes/0.25#installation", "title": "Zero 0.25", "searchTitle": "Installation", "sectionTitle": "Installation", @@ -4575,7 +4589,7 @@ "kind": "section" }, { - "id": "370-release-notes/0.25#overview", + "id": "372-release-notes/0.25#overview", "title": "Zero 0.25", "searchTitle": "Overview", "sectionTitle": "Overview", @@ -4585,7 +4599,7 @@ "kind": "section" }, { - "id": "371-release-notes/0.25#upgrading", + "id": "373-release-notes/0.25#upgrading", "title": "Zero 0.25", "searchTitle": "Upgrading", "sectionTitle": "Upgrading", @@ -4595,7 +4609,7 @@ "kind": "section" }, { - "id": "372-release-notes/0.25#features", + "id": "374-release-notes/0.25#features", "title": "Zero 0.25", "searchTitle": "Features", "sectionTitle": "Features", @@ -4605,7 +4619,7 @@ "kind": "section" }, { - "id": "373-release-notes/0.25#performance", + "id": "375-release-notes/0.25#performance", "title": "Zero 0.25", "searchTitle": "Performance", "sectionTitle": "Performance", @@ -4615,7 +4629,7 @@ "kind": "section" }, { - "id": "374-release-notes/0.25#fixes", + "id": "376-release-notes/0.25#fixes", "title": "Zero 0.25", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -4625,7 +4639,7 @@ "kind": "section" }, { - "id": "375-release-notes/0.25#breaking-changes", + "id": "377-release-notes/0.25#breaking-changes", "title": "Zero 0.25", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -4661,7 +4675,7 @@ "kind": "page" }, { - "id": "376-release-notes/0.26#installation", + "id": "378-release-notes/0.26#installation", "title": "Zero 0.26", "searchTitle": "Installation", "sectionTitle": "Installation", @@ -4671,7 +4685,7 @@ "kind": "section" }, { - "id": "377-release-notes/0.26#features", + "id": "379-release-notes/0.26#features", "title": "Zero 0.26", "searchTitle": "Features", "sectionTitle": "Features", @@ -4681,7 +4695,7 @@ "kind": "section" }, { - "id": "378-release-notes/0.26#fixes", + "id": "380-release-notes/0.26#fixes", "title": "Zero 0.26", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -4691,7 +4705,7 @@ "kind": "section" }, { - "id": "379-release-notes/0.26#breaking-changes", + "id": "381-release-notes/0.26#breaking-changes", "title": "Zero 0.26", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -4735,7 +4749,7 @@ "kind": "page" }, { - "id": "380-release-notes/0.3#install", + "id": "382-release-notes/0.3#install", "title": "Zero 0.3", "searchTitle": "Install", "sectionTitle": "Install", @@ -4745,7 +4759,7 @@ "kind": "section" }, { - "id": "381-release-notes/0.3#breaking-changes", + "id": "383-release-notes/0.3#breaking-changes", "title": "Zero 0.3", "searchTitle": "Breaking changes", "sectionTitle": "Breaking changes", @@ -4755,7 +4769,7 @@ "kind": "section" }, { - "id": "382-release-notes/0.3#features", + "id": "384-release-notes/0.3#features", "title": "Zero 0.3", "searchTitle": "Features", "sectionTitle": "Features", @@ -4765,7 +4779,7 @@ "kind": "section" }, { - "id": "383-release-notes/0.3#fixes", + "id": "385-release-notes/0.3#fixes", "title": "Zero 0.3", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -4775,7 +4789,7 @@ "kind": "section" }, { - "id": "384-release-notes/0.3#docs", + "id": "386-release-notes/0.3#docs", "title": "Zero 0.3", "searchTitle": "Docs", "sectionTitle": "Docs", @@ -4785,7 +4799,7 @@ "kind": "section" }, { - "id": "385-release-notes/0.3#zbugs", + "id": "387-release-notes/0.3#zbugs", "title": "Zero 0.3", "searchTitle": "zbugs", "sectionTitle": "zbugs", @@ -4829,7 +4843,7 @@ "kind": "page" }, { - "id": "386-release-notes/0.4#install", + "id": "388-release-notes/0.4#install", "title": "Zero 0.4", "searchTitle": "Install", "sectionTitle": "Install", @@ -4839,7 +4853,7 @@ "kind": "section" }, { - "id": "387-release-notes/0.4#breaking-changes", + "id": "389-release-notes/0.4#breaking-changes", "title": "Zero 0.4", "searchTitle": "Breaking changes", "sectionTitle": "Breaking changes", @@ -4849,7 +4863,7 @@ "kind": "section" }, { - "id": "388-release-notes/0.4#added-or--and--and-not-to-zql-documentation", + "id": "390-release-notes/0.4#added-or--and--and-not-to-zql-documentation", "title": "Zero 0.4", "searchTitle": "Added or , and , and not to ZQL (documentation).", "sectionTitle": "Added or , and , and not to ZQL (documentation).", @@ -4859,7 +4873,7 @@ "kind": "section" }, { - "id": "389-release-notes/0.4#fixes", + "id": "391-release-notes/0.4#fixes", "title": "Zero 0.4", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -4869,7 +4883,7 @@ "kind": "section" }, { - "id": "390-release-notes/0.4#docs", + "id": "392-release-notes/0.4#docs", "title": "Zero 0.4", "searchTitle": "Docs", "sectionTitle": "Docs", @@ -4879,7 +4893,7 @@ "kind": "section" }, { - "id": "391-release-notes/0.4#zbugs", + "id": "393-release-notes/0.4#zbugs", "title": "Zero 0.4", "searchTitle": "zbugs", "sectionTitle": "zbugs", @@ -4923,7 +4937,7 @@ "kind": "page" }, { - "id": "392-release-notes/0.5#install", + "id": "394-release-notes/0.5#install", "title": "Zero 0.5", "searchTitle": "Install", "sectionTitle": "Install", @@ -4933,7 +4947,7 @@ "kind": "section" }, { - "id": "393-release-notes/0.5#breaking-changes", + "id": "395-release-notes/0.5#breaking-changes", "title": "Zero 0.5", "searchTitle": "Breaking changes", "sectionTitle": "Breaking changes", @@ -4943,7 +4957,7 @@ "kind": "section" }, { - "id": "394-release-notes/0.5#features", + "id": "396-release-notes/0.5#features", "title": "Zero 0.5", "searchTitle": "Features", "sectionTitle": "Features", @@ -4953,7 +4967,7 @@ "kind": "section" }, { - "id": "395-release-notes/0.5#fixes", + "id": "397-release-notes/0.5#fixes", "title": "Zero 0.5", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -4963,7 +4977,7 @@ "kind": "section" }, { - "id": "396-release-notes/0.5#docs", + "id": "398-release-notes/0.5#docs", "title": "Zero 0.5", "searchTitle": "Docs", "sectionTitle": "Docs", @@ -4973,7 +4987,7 @@ "kind": "section" }, { - "id": "397-release-notes/0.5#zbugs", + "id": "399-release-notes/0.5#zbugs", "title": "Zero 0.5", "searchTitle": "zbugs", "sectionTitle": "zbugs", @@ -5017,7 +5031,7 @@ "kind": "page" }, { - "id": "398-release-notes/0.6#install", + "id": "400-release-notes/0.6#install", "title": "Zero 0.6", "searchTitle": "Install", "sectionTitle": "Install", @@ -5027,7 +5041,7 @@ "kind": "section" }, { - "id": "399-release-notes/0.6#upgrade-guide", + "id": "401-release-notes/0.6#upgrade-guide", "title": "Zero 0.6", "searchTitle": "Upgrade Guide", "sectionTitle": "Upgrade Guide", @@ -5037,7 +5051,7 @@ "kind": "section" }, { - "id": "400-release-notes/0.6#breaking-changes", + "id": "402-release-notes/0.6#breaking-changes", "title": "Zero 0.6", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -5047,7 +5061,7 @@ "kind": "section" }, { - "id": "401-release-notes/0.6#features", + "id": "403-release-notes/0.6#features", "title": "Zero 0.6", "searchTitle": "Features", "sectionTitle": "Features", @@ -5057,7 +5071,7 @@ "kind": "section" }, { - "id": "402-release-notes/0.6#zbugs", + "id": "404-release-notes/0.6#zbugs", "title": "Zero 0.6", "searchTitle": "zbugs", "sectionTitle": "zbugs", @@ -5067,7 +5081,7 @@ "kind": "section" }, { - "id": "403-release-notes/0.6#docs", + "id": "405-release-notes/0.6#docs", "title": "Zero 0.6", "searchTitle": "Docs", "sectionTitle": "Docs", @@ -5107,7 +5121,7 @@ "kind": "page" }, { - "id": "404-release-notes/0.7#install", + "id": "406-release-notes/0.7#install", "title": "Zero 0.7", "searchTitle": "Install", "sectionTitle": "Install", @@ -5117,7 +5131,7 @@ "kind": "section" }, { - "id": "405-release-notes/0.7#features", + "id": "407-release-notes/0.7#features", "title": "Zero 0.7", "searchTitle": "Features", "sectionTitle": "Features", @@ -5127,7 +5141,7 @@ "kind": "section" }, { - "id": "406-release-notes/0.7#breaking-changes", + "id": "408-release-notes/0.7#breaking-changes", "title": "Zero 0.7", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -5137,7 +5151,7 @@ "kind": "section" }, { - "id": "407-release-notes/0.7#zbugs", + "id": "409-release-notes/0.7#zbugs", "title": "Zero 0.7", "searchTitle": "zbugs", "sectionTitle": "zbugs", @@ -5147,7 +5161,7 @@ "kind": "section" }, { - "id": "408-release-notes/0.7#docs", + "id": "410-release-notes/0.7#docs", "title": "Zero 0.7", "searchTitle": "Docs", "sectionTitle": "Docs", @@ -5183,7 +5197,7 @@ "kind": "page" }, { - "id": "409-release-notes/0.8#install", + "id": "411-release-notes/0.8#install", "title": "Zero 0.8", "searchTitle": "Install", "sectionTitle": "Install", @@ -5193,7 +5207,7 @@ "kind": "section" }, { - "id": "410-release-notes/0.8#features", + "id": "412-release-notes/0.8#features", "title": "Zero 0.8", "searchTitle": "Features", "sectionTitle": "Features", @@ -5203,7 +5217,7 @@ "kind": "section" }, { - "id": "411-release-notes/0.8#fixes", + "id": "413-release-notes/0.8#fixes", "title": "Zero 0.8", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -5213,7 +5227,7 @@ "kind": "section" }, { - "id": "412-release-notes/0.8#breaking-changes", + "id": "414-release-notes/0.8#breaking-changes", "title": "Zero 0.8", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -5249,7 +5263,7 @@ "kind": "page" }, { - "id": "413-release-notes/0.9#install", + "id": "415-release-notes/0.9#install", "title": "Zero 0.9", "searchTitle": "Install", "sectionTitle": "Install", @@ -5259,7 +5273,7 @@ "kind": "section" }, { - "id": "414-release-notes/0.9#features", + "id": "416-release-notes/0.9#features", "title": "Zero 0.9", "searchTitle": "Features", "sectionTitle": "Features", @@ -5269,7 +5283,7 @@ "kind": "section" }, { - "id": "415-release-notes/0.9#fixes", + "id": "417-release-notes/0.9#fixes", "title": "Zero 0.9", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -5279,7 +5293,7 @@ "kind": "section" }, { - "id": "416-release-notes/0.9#breaking-changes", + "id": "418-release-notes/0.9#breaking-changes", "title": "Zero 0.9", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -5319,7 +5333,7 @@ "kind": "page" }, { - "id": "417-release-notes/1.0#installation", + "id": "419-release-notes/1.0#installation", "title": "Zero 1.0", "searchTitle": "Installation", "sectionTitle": "Installation", @@ -5329,7 +5343,7 @@ "kind": "section" }, { - "id": "418-release-notes/1.0#overview", + "id": "420-release-notes/1.0#overview", "title": "Zero 1.0", "searchTitle": "Overview", "sectionTitle": "Overview", @@ -5339,7 +5353,7 @@ "kind": "section" }, { - "id": "419-release-notes/1.0#features", + "id": "421-release-notes/1.0#features", "title": "Zero 1.0", "searchTitle": "Features", "sectionTitle": "Features", @@ -5349,7 +5363,7 @@ "kind": "section" }, { - "id": "420-release-notes/1.0#fixes", + "id": "422-release-notes/1.0#fixes", "title": "Zero 1.0", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -5359,7 +5373,7 @@ "kind": "section" }, { - "id": "421-release-notes/1.0#breaking-changes", + "id": "423-release-notes/1.0#breaking-changes", "title": "Zero 1.0", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -5395,7 +5409,7 @@ "kind": "page" }, { - "id": "422-release-notes/1.1#installation", + "id": "424-release-notes/1.1#installation", "title": "Zero 1.1", "searchTitle": "Installation", "sectionTitle": "Installation", @@ -5405,7 +5419,7 @@ "kind": "section" }, { - "id": "423-release-notes/1.1#features", + "id": "425-release-notes/1.1#features", "title": "Zero 1.1", "searchTitle": "Features", "sectionTitle": "Features", @@ -5415,7 +5429,7 @@ "kind": "section" }, { - "id": "424-release-notes/1.1#fixes", + "id": "426-release-notes/1.1#fixes", "title": "Zero 1.1", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -5425,7 +5439,7 @@ "kind": "section" }, { - "id": "425-release-notes/1.1#breaking-changes", + "id": "427-release-notes/1.1#breaking-changes", "title": "Zero 1.1", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -5465,7 +5479,7 @@ "kind": "page" }, { - "id": "426-release-notes/1.2#installation", + "id": "428-release-notes/1.2#installation", "title": "Zero 1.2", "searchTitle": "Installation", "sectionTitle": "Installation", @@ -5475,7 +5489,7 @@ "kind": "section" }, { - "id": "427-release-notes/1.2#features", + "id": "429-release-notes/1.2#features", "title": "Zero 1.2", "searchTitle": "Features", "sectionTitle": "Features", @@ -5485,7 +5499,7 @@ "kind": "section" }, { - "id": "428-release-notes/1.2#performance", + "id": "430-release-notes/1.2#performance", "title": "Zero 1.2", "searchTitle": "Performance", "sectionTitle": "Performance", @@ -5495,7 +5509,7 @@ "kind": "section" }, { - "id": "429-release-notes/1.2#fixes", + "id": "431-release-notes/1.2#fixes", "title": "Zero 1.2", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -5505,7 +5519,7 @@ "kind": "section" }, { - "id": "430-release-notes/1.2#breaking-changes", + "id": "432-release-notes/1.2#breaking-changes", "title": "Zero 1.2", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -5545,7 +5559,7 @@ "kind": "page" }, { - "id": "431-release-notes/1.3#installation", + "id": "433-release-notes/1.3#installation", "title": "Zero 1.3", "searchTitle": "Installation", "sectionTitle": "Installation", @@ -5555,7 +5569,7 @@ "kind": "section" }, { - "id": "432-release-notes/1.3#features", + "id": "434-release-notes/1.3#features", "title": "Zero 1.3", "searchTitle": "Features", "sectionTitle": "Features", @@ -5565,7 +5579,7 @@ "kind": "section" }, { - "id": "433-release-notes/1.3#performance", + "id": "435-release-notes/1.3#performance", "title": "Zero 1.3", "searchTitle": "Performance", "sectionTitle": "Performance", @@ -5575,7 +5589,7 @@ "kind": "section" }, { - "id": "434-release-notes/1.3#fixes", + "id": "436-release-notes/1.3#fixes", "title": "Zero 1.3", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -5585,7 +5599,7 @@ "kind": "section" }, { - "id": "435-release-notes/1.3#breaking-changes", + "id": "437-release-notes/1.3#breaking-changes", "title": "Zero 1.3", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -5633,7 +5647,7 @@ "kind": "page" }, { - "id": "436-release-notes/1.4#installation", + "id": "438-release-notes/1.4#installation", "title": "Zero 1.4", "searchTitle": "Installation", "sectionTitle": "Installation", @@ -5643,7 +5657,7 @@ "kind": "section" }, { - "id": "437-release-notes/1.4#upgrading", + "id": "439-release-notes/1.4#upgrading", "title": "Zero 1.4", "searchTitle": "Upgrading", "sectionTitle": "Upgrading", @@ -5653,7 +5667,7 @@ "kind": "section" }, { - "id": "438-release-notes/1.4#userid-anon", + "id": "440-release-notes/1.4#userid-anon", "title": "Zero 1.4", "searchTitle": "userID: \"anon\"", "sectionTitle": "userID: \"anon\"", @@ -5663,7 +5677,7 @@ "kind": "section" }, { - "id": "439-release-notes/1.4#features", + "id": "441-release-notes/1.4#features", "title": "Zero 1.4", "searchTitle": "Features", "sectionTitle": "Features", @@ -5673,7 +5687,7 @@ "kind": "section" }, { - "id": "440-release-notes/1.4#performance", + "id": "442-release-notes/1.4#performance", "title": "Zero 1.4", "searchTitle": "Performance", "sectionTitle": "Performance", @@ -5683,7 +5697,7 @@ "kind": "section" }, { - "id": "441-release-notes/1.4#fixes", + "id": "443-release-notes/1.4#fixes", "title": "Zero 1.4", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -5693,7 +5707,7 @@ "kind": "section" }, { - "id": "442-release-notes/1.4#breaking-changes", + "id": "444-release-notes/1.4#breaking-changes", "title": "Zero 1.4", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -5745,7 +5759,7 @@ "kind": "page" }, { - "id": "443-release-notes/1.5#installation", + "id": "445-release-notes/1.5#installation", "title": "Zero 1.5", "searchTitle": "Installation", "sectionTitle": "Installation", @@ -5755,7 +5769,7 @@ "kind": "section" }, { - "id": "444-release-notes/1.5#upgrading", + "id": "446-release-notes/1.5#upgrading", "title": "Zero 1.5", "searchTitle": "Upgrading", "sectionTitle": "Upgrading", @@ -5765,7 +5779,7 @@ "kind": "section" }, { - "id": "445-release-notes/1.5#authenticated-client-groups", + "id": "447-release-notes/1.5#authenticated-client-groups", "title": "Zero 1.5", "searchTitle": "Authenticated Client Groups", "sectionTitle": "Authenticated Client Groups", @@ -5775,7 +5789,7 @@ "kind": "section" }, { - "id": "446-release-notes/1.5#deploy-order", + "id": "448-release-notes/1.5#deploy-order", "title": "Zero 1.5", "searchTitle": "Deploy Order", "sectionTitle": "Deploy Order", @@ -5785,7 +5799,7 @@ "kind": "section" }, { - "id": "447-release-notes/1.5#features", + "id": "449-release-notes/1.5#features", "title": "Zero 1.5", "searchTitle": "Features", "sectionTitle": "Features", @@ -5795,7 +5809,7 @@ "kind": "section" }, { - "id": "448-release-notes/1.5#performance", + "id": "450-release-notes/1.5#performance", "title": "Zero 1.5", "searchTitle": "Performance", "sectionTitle": "Performance", @@ -5805,7 +5819,7 @@ "kind": "section" }, { - "id": "449-release-notes/1.5#fixes", + "id": "451-release-notes/1.5#fixes", "title": "Zero 1.5", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -5815,7 +5829,7 @@ "kind": "section" }, { - "id": "450-release-notes/1.5#breaking-changes", + "id": "452-release-notes/1.5#breaking-changes", "title": "Zero 1.5", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -5863,7 +5877,7 @@ "kind": "page" }, { - "id": "451-release-notes/1.6#installation", + "id": "453-release-notes/1.6#installation", "title": "Zero 1.6", "searchTitle": "Installation", "sectionTitle": "Installation", @@ -5873,7 +5887,7 @@ "kind": "section" }, { - "id": "452-release-notes/1.6#upgrading", + "id": "454-release-notes/1.6#upgrading", "title": "Zero 1.6", "searchTitle": "Upgrading", "sectionTitle": "Upgrading", @@ -5883,7 +5897,7 @@ "kind": "section" }, { - "id": "453-release-notes/1.6#planetscale-failover", + "id": "455-release-notes/1.6#planetscale-failover", "title": "Zero 1.6", "searchTitle": "PlanetScale Failover", "sectionTitle": "PlanetScale Failover", @@ -5893,7 +5907,7 @@ "kind": "section" }, { - "id": "454-release-notes/1.6#features", + "id": "456-release-notes/1.6#features", "title": "Zero 1.6", "searchTitle": "Features", "sectionTitle": "Features", @@ -5903,7 +5917,7 @@ "kind": "section" }, { - "id": "455-release-notes/1.6#performance", + "id": "457-release-notes/1.6#performance", "title": "Zero 1.6", "searchTitle": "Performance", "sectionTitle": "Performance", @@ -5913,7 +5927,7 @@ "kind": "section" }, { - "id": "456-release-notes/1.6#fixes", + "id": "458-release-notes/1.6#fixes", "title": "Zero 1.6", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -5923,7 +5937,7 @@ "kind": "section" }, { - "id": "457-release-notes/1.6#breaking-changes", + "id": "459-release-notes/1.6#breaking-changes", "title": "Zero 1.6", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -5975,7 +5989,7 @@ "kind": "page" }, { - "id": "458-release-notes/1.7#installation", + "id": "460-release-notes/1.7#installation", "title": "Zero 1.7", "searchTitle": "Installation", "sectionTitle": "Installation", @@ -5985,7 +5999,7 @@ "kind": "section" }, { - "id": "459-release-notes/1.7#overview", + "id": "461-release-notes/1.7#overview", "title": "Zero 1.7", "searchTitle": "Overview", "sectionTitle": "Overview", @@ -5995,7 +6009,7 @@ "kind": "section" }, { - "id": "460-release-notes/1.7#features", + "id": "462-release-notes/1.7#features", "title": "Zero 1.7", "searchTitle": "Features", "sectionTitle": "Features", @@ -6005,7 +6019,7 @@ "kind": "section" }, { - "id": "461-release-notes/1.7#performance", + "id": "463-release-notes/1.7#performance", "title": "Zero 1.7", "searchTitle": "Performance", "sectionTitle": "Performance", @@ -6015,7 +6029,7 @@ "kind": "section" }, { - "id": "462-release-notes/1.7#replication", + "id": "464-release-notes/1.7#replication", "title": "Zero 1.7", "searchTitle": "Replication", "sectionTitle": "Replication", @@ -6025,7 +6039,7 @@ "kind": "section" }, { - "id": "463-release-notes/1.7#flipped-exists-queries", + "id": "465-release-notes/1.7#flipped-exists-queries", "title": "Zero 1.7", "searchTitle": "Flipped Exists Queries", "sectionTitle": "Flipped Exists Queries", @@ -6035,7 +6049,7 @@ "kind": "section" }, { - "id": "464-release-notes/1.7#fixes", + "id": "466-release-notes/1.7#fixes", "title": "Zero 1.7", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -6045,7 +6059,7 @@ "kind": "section" }, { - "id": "465-release-notes/1.7#breaking-changes", + "id": "467-release-notes/1.7#breaking-changes", "title": "Zero 1.7", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -6059,7 +6073,7 @@ "title": "Zero 1.8", "searchTitle": "Zero 1.8", "url": "/docs/release-notes/1.8", - "content": "Installation npm install @rocicorp/zero@1.8 You can now use zero-cache from GHCR: docker pull rocicorp/zero:1.8.0 # or docker pull ghcr.io/rocicorp/zero:1.8.0 Overview Zero 1.8 improves observability, performance, and reliability. Features Request-header forwarding: zero-cache can forward selected WebSocket upgrade headers to custom APIs using ZERO_MUTATE_ALLOWED_REQUEST_HEADERS and ZERO_QUERY_ALLOWED_REQUEST_HEADERS. (#6144, thanks @tjenkinson!) GHCR Docker images: Zero images are now published to ghcr.io/rocicorp/zero as well as Docker Hub. (#6161) Mutator result type: MutatorResult is now exported from @rocicorp/zero for typing helpers that await .client or .server. (#6223) Operational metrics: zero-cache adds metrics for API calls and startup, initial sync and replication slots, and Litestream backup and restore. (#6203, #6208, #6191, #6199, #6210) Stability metrics: New serving-lag, CVR, and WebSocket metrics and replication flow-control metrics help diagnose delayed updates, reconnects, and backpressure. (#6157, #6214, #6207) Performance Zero 1.8 speeds up replication of large transactions, maintenance of queries with orderBy() and limit(), and local ZQL queries with related(). Replicating Large Transactions Bulk imports, backfills, or migrations often change thousands of rows in a single transaction. Zero 1.8 replicates these transactions about 50% faster. Maintaining orderBy() + limit() Queries For example, an app might show the first 50 open issues, ordered by priority: zql.issue .where('workspaceID', workspaceID) .where('status', 'open') .orderBy('priority', 'desc') .orderBy('created', 'asc') .limit(50) If only a few issues are open, the 50th matching issue can be far down the orderBy() index. When changes move rows in or out of the first 50 results, Zero may need to read more rows after the last row currently returned. Previously, that read could start at the beginning of the index, even though rows before the last returned row could not be next. In 1.8, Zero starts SQLite at the last returned row's sort key, so SQLite can seek into the index and scan from there. When the last returned row was 50,000 rows into the index, incremental updates rose from 248 to 521 updates/sec. At the end of a 100,000-row index, they rose from 250 to 14,977 updates/sec. Running Local ZQL Queries When a local query first runs, Zero hydrates it from data already on the client. Zero 1.8 makes that faster, especially for queries with relationships. For example: zql.issue.related('creator').related('comments') Hydrating 500 issues with creators and comments fell from 2.54 ms to 1.97 ms. Hydrating 500 issues with creators fell from 1.11 ms to 0.84 ms. Fixes Logical replication now reconnects when the inbound Postgres stream goes silent. Postgres writes no longer use sockets after disconnection. The Drizzle adapter now handles array-mode results from Drizzle 1.0 RC prepareQuery. (thanks @typedrat!) z2s now compiles queries using start, and SQLite fetches handle null start-cursor fields. Queries no longer appear complete with stale or empty results after reconnect. React Native reads now work with op-sqlite v17. View-syncers no longer fail while the first backup is uploading or retry before a restorable backup exists on cold start. Zero Docker images now choose the correct default sync-worker count. Change-stream catch-up now respects flow control, preventing unbounded in-memory backlogs. Breaking Changes None.", + "content": "Installation npm install @rocicorp/zero@1.8 You can now use zero-cache from GHCR: docker pull rocicorp/zero:1.8.0 # or docker pull ghcr.io/rocicorp/zero:1.8.0 Overview Zero 1.8 improves observability, performance, and reliability. Features Request-header forwarding: zero-cache can forward selected WebSocket upgrade headers to custom APIs using ZERO_MUTATE_ALLOWED_REQUEST_HEADERS and ZERO_QUERY_ALLOWED_REQUEST_HEADERS. (#6144, thanks @tjenkinson!) GHCR Docker images: Zero images are now published to ghcr.io/rocicorp/zero as well as Docker Hub. (#6161) Mutator result type: MutatorResult is now exported from @rocicorp/zero for typing helpers that await .client or .server. (#6223) Operational metrics: zero-cache adds metrics for API calls and startup, initial sync and replication slots, and Litestream backup and restore. (#6203, #6208, #6191, #6199, #6210) Stability metrics: New serving-lag, CVR, and WebSocket metrics and replication flow-control metrics help diagnose delayed updates, reconnects, and backpressure. (#6157, #6214, #6207) Performance Zero 1.8 speeds up replication of large transactions, maintenance of queries that use limit(), and client-side query hydration. Replicating Large Transactions Bulk imports, backfills, or migrations often change thousands of rows in a single Postgres transaction. These large transactions replicate about 1.5x faster in Zero 1.8. Maintaining limit() Queries Consider a query like this: zql.issue .where('status', 'open') .orderBy('created', 'asc') .limit(50) Zero can fulfill this query using an index on either status or created. If it decides to use the created index, Zero might have to consider many rows before it finds 50 matches. That is unavoidable. But when changes to the data move rows in or out of the first 50 results, Zero 1.7 repeated the work to find the first 50 results, making incremental updates slower than necessary. Zero 1.8 fixes this. In benchmarks, when the last returned row was 50,000 rows into the index, incremental updates were 2x faster in Zero 1.8. When it was 100,000 rows in, updates were over 50x faster in Zero 1.8. Client-Side Hydration Zero runs queries first on the client, then on the server. The initial client-side hydration got faster in Zero 1.8. For example, this query returns initial data from client about 1.3x faster in Zero 1.8: zql.issue.related('creator').related('comments') Fixes Logical replication now reconnects when the inbound Postgres stream goes silent. Postgres writes no longer use sockets after disconnection. The Drizzle adapter now handles array-mode results from Drizzle 1.0 RC prepareQuery. (thanks @typedrat!) z2s now compiles queries using start, and SQLite fetches handle null start-cursor fields. Queries no longer appear complete with stale or empty results after reconnect. React Native reads now work with op-sqlite v17. View-syncers no longer fail while the first backup is uploading or retry before a restorable backup exists on cold start. Zero Docker images now choose the correct default sync-worker count. Change-stream catch-up now respects flow control, preventing unbounded in-memory backlogs. Breaking Changes None.", "headings": [ { "text": "Installation", @@ -6082,12 +6096,12 @@ "id": "replicating-large-transactions" }, { - "text": "Maintaining orderBy() + limit() Queries", - "id": "maintaining-orderby--limit-queries" + "text": "Maintaining limit() Queries", + "id": "maintaining-limit-queries" }, { - "text": "Running Local ZQL Queries", - "id": "running-local-zql-queries" + "text": "Client-Side Hydration", + "id": "client-side-hydration" }, { "text": "Fixes", @@ -6101,7 +6115,7 @@ "kind": "page" }, { - "id": "466-release-notes/1.8#installation", + "id": "468-release-notes/1.8#installation", "title": "Zero 1.8", "searchTitle": "Installation", "sectionTitle": "Installation", @@ -6111,7 +6125,7 @@ "kind": "section" }, { - "id": "467-release-notes/1.8#overview", + "id": "469-release-notes/1.8#overview", "title": "Zero 1.8", "searchTitle": "Overview", "sectionTitle": "Overview", @@ -6121,7 +6135,7 @@ "kind": "section" }, { - "id": "468-release-notes/1.8#features", + "id": "470-release-notes/1.8#features", "title": "Zero 1.8", "searchTitle": "Features", "sectionTitle": "Features", @@ -6131,47 +6145,47 @@ "kind": "section" }, { - "id": "469-release-notes/1.8#performance", + "id": "471-release-notes/1.8#performance", "title": "Zero 1.8", "searchTitle": "Performance", "sectionTitle": "Performance", "sectionId": "performance", "url": "/docs/release-notes/1.8", - "content": "Zero 1.8 speeds up replication of large transactions, maintenance of queries with orderBy() and limit(), and local ZQL queries with related(). Replicating Large Transactions Bulk imports, backfills, or migrations often change thousands of rows in a single transaction. Zero 1.8 replicates these transactions about 50% faster. Maintaining orderBy() + limit() Queries For example, an app might show the first 50 open issues, ordered by priority: zql.issue .where('workspaceID', workspaceID) .where('status', 'open') .orderBy('priority', 'desc') .orderBy('created', 'asc') .limit(50) If only a few issues are open, the 50th matching issue can be far down the orderBy() index. When changes move rows in or out of the first 50 results, Zero may need to read more rows after the last row currently returned. Previously, that read could start at the beginning of the index, even though rows before the last returned row could not be next. In 1.8, Zero starts SQLite at the last returned row's sort key, so SQLite can seek into the index and scan from there. When the last returned row was 50,000 rows into the index, incremental updates rose from 248 to 521 updates/sec. At the end of a 100,000-row index, they rose from 250 to 14,977 updates/sec. Running Local ZQL Queries When a local query first runs, Zero hydrates it from data already on the client. Zero 1.8 makes that faster, especially for queries with relationships. For example: zql.issue.related('creator').related('comments') Hydrating 500 issues with creators and comments fell from 2.54 ms to 1.97 ms. Hydrating 500 issues with creators fell from 1.11 ms to 0.84 ms.", + "content": "Zero 1.8 speeds up replication of large transactions, maintenance of queries that use limit(), and client-side query hydration. Replicating Large Transactions Bulk imports, backfills, or migrations often change thousands of rows in a single Postgres transaction. These large transactions replicate about 1.5x faster in Zero 1.8. Maintaining limit() Queries Consider a query like this: zql.issue .where('status', 'open') .orderBy('created', 'asc') .limit(50) Zero can fulfill this query using an index on either status or created. If it decides to use the created index, Zero might have to consider many rows before it finds 50 matches. That is unavoidable. But when changes to the data move rows in or out of the first 50 results, Zero 1.7 repeated the work to find the first 50 results, making incremental updates slower than necessary. Zero 1.8 fixes this. In benchmarks, when the last returned row was 50,000 rows into the index, incremental updates were 2x faster in Zero 1.8. When it was 100,000 rows in, updates were over 50x faster in Zero 1.8. Client-Side Hydration Zero runs queries first on the client, then on the server. The initial client-side hydration got faster in Zero 1.8. For example, this query returns initial data from client about 1.3x faster in Zero 1.8: zql.issue.related('creator').related('comments')", "kind": "section" }, { - "id": "470-release-notes/1.8#replicating-large-transactions", + "id": "472-release-notes/1.8#replicating-large-transactions", "title": "Zero 1.8", "searchTitle": "Replicating Large Transactions", "sectionTitle": "Replicating Large Transactions", "sectionId": "replicating-large-transactions", "url": "/docs/release-notes/1.8", - "content": "Bulk imports, backfills, or migrations often change thousands of rows in a single transaction. Zero 1.8 replicates these transactions about 50% faster.", + "content": "Bulk imports, backfills, or migrations often change thousands of rows in a single Postgres transaction. These large transactions replicate about 1.5x faster in Zero 1.8.", "kind": "section" }, { - "id": "471-release-notes/1.8#maintaining-orderby--limit-queries", + "id": "473-release-notes/1.8#maintaining-limit-queries", "title": "Zero 1.8", - "searchTitle": "Maintaining orderBy() + limit() Queries", - "sectionTitle": "Maintaining orderBy() + limit() Queries", - "sectionId": "maintaining-orderby--limit-queries", + "searchTitle": "Maintaining limit() Queries", + "sectionTitle": "Maintaining limit() Queries", + "sectionId": "maintaining-limit-queries", "url": "/docs/release-notes/1.8", - "content": "For example, an app might show the first 50 open issues, ordered by priority: zql.issue .where('workspaceID', workspaceID) .where('status', 'open') .orderBy('priority', 'desc') .orderBy('created', 'asc') .limit(50) If only a few issues are open, the 50th matching issue can be far down the orderBy() index. When changes move rows in or out of the first 50 results, Zero may need to read more rows after the last row currently returned. Previously, that read could start at the beginning of the index, even though rows before the last returned row could not be next. In 1.8, Zero starts SQLite at the last returned row's sort key, so SQLite can seek into the index and scan from there. When the last returned row was 50,000 rows into the index, incremental updates rose from 248 to 521 updates/sec. At the end of a 100,000-row index, they rose from 250 to 14,977 updates/sec.", + "content": "Consider a query like this: zql.issue .where('status', 'open') .orderBy('created', 'asc') .limit(50) Zero can fulfill this query using an index on either status or created. If it decides to use the created index, Zero might have to consider many rows before it finds 50 matches. That is unavoidable. But when changes to the data move rows in or out of the first 50 results, Zero 1.7 repeated the work to find the first 50 results, making incremental updates slower than necessary. Zero 1.8 fixes this. In benchmarks, when the last returned row was 50,000 rows into the index, incremental updates were 2x faster in Zero 1.8. When it was 100,000 rows in, updates were over 50x faster in Zero 1.8.", "kind": "section" }, { - "id": "472-release-notes/1.8#running-local-zql-queries", + "id": "474-release-notes/1.8#client-side-hydration", "title": "Zero 1.8", - "searchTitle": "Running Local ZQL Queries", - "sectionTitle": "Running Local ZQL Queries", - "sectionId": "running-local-zql-queries", + "searchTitle": "Client-Side Hydration", + "sectionTitle": "Client-Side Hydration", + "sectionId": "client-side-hydration", "url": "/docs/release-notes/1.8", - "content": "When a local query first runs, Zero hydrates it from data already on the client. Zero 1.8 makes that faster, especially for queries with relationships. For example: zql.issue.related('creator').related('comments') Hydrating 500 issues with creators and comments fell from 2.54 ms to 1.97 ms. Hydrating 500 issues with creators fell from 1.11 ms to 0.84 ms.", + "content": "Zero runs queries first on the client, then on the server. The initial client-side hydration got faster in Zero 1.8. For example, this query returns initial data from client about 1.3x faster in Zero 1.8: zql.issue.related('creator').related('comments')", "kind": "section" }, { - "id": "473-release-notes/1.8#fixes", + "id": "475-release-notes/1.8#fixes", "title": "Zero 1.8", "searchTitle": "Fixes", "sectionTitle": "Fixes", @@ -6181,7 +6195,7 @@ "kind": "section" }, { - "id": "474-release-notes/1.8#breaking-changes", + "id": "476-release-notes/1.8#breaking-changes", "title": "Zero 1.8", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -6191,16 +6205,110 @@ "kind": "section" }, { - "id": "61-release-notes", + "id": "61-release-notes/1.9", + "title": "Zero 1.9", + "searchTitle": "Zero 1.9", + "url": "/docs/release-notes/1.9", + "content": "Installation npm install @rocicorp/zero@1.9 You can use zero-cache from Docker Hub or GHCR: docker pull rocicorp/zero:1.9.0 # or docker pull ghcr.io/rocicorp/zero:1.9.0 Overview Zero 1.9 improves query correctness and zero-cache reliability. Performance Deferred. Fixes Ordered queries now paginate and maintain windows correctly when cursor fields contain NULL, including compound tie-break fields and reverse walks. This prevents skipped rows, empty windows, and related Bound should be set failures. (thanks @YevheniiKotyrlo!) Schema construction, CRUD mutators, and materialized views now preserve a key named __proto__ as user data instead of invoking JavaScript's legacy prototype setter. (thanks @tjenkinson!) Clients now receive changed rows after a server-side query is rebuilt, instead of retaining stale results in a rare rehydration case. zero-cache now bounds its SQLite prepared-statement caches with LRU eviction, preventing unbounded statement retention when applications generate many distinct query shapes. Replication lag reports now retry when an expected report is missing, and serving-lag metrics exclude disconnected or not-yet-validated client groups. See the updated OpenTelemetry metric descriptions. zero-cache now detects and resets PostgreSQL connections that stop carrying wire traffic, including over TLS, allowing work to recover from proxy-created half-open sockets. See Breaking Changes. zero-cache now releases custom-query caches when client groups stop, preventing inactive groups from retaining timers and transformed queries. Breaking Changes PostgreSQL Socket Inactivity Timeout zero-cache now monitors wire activity on its PostgreSQL connections. By default, it checks every two minutes and resets a connection after one to two inactive intervals. This recovers half-open connections, but can interrupt a long-running statement that legitimately produces no network traffic. If legitimate Postgres operations can remain silent for this long, set ZERO_PG_SOCKET_INACTIVITY_TIMEOUT on zero-cache to a longer interval in milliseconds. Set it to 0 to disable the watchdog.", + "headings": [ + { + "text": "Installation", + "id": "installation" + }, + { + "text": "Overview", + "id": "overview" + }, + { + "text": "Performance", + "id": "performance" + }, + { + "text": "Fixes", + "id": "fixes" + }, + { + "text": "Breaking Changes", + "id": "breaking-changes" + }, + { + "text": "PostgreSQL Socket Inactivity Timeout", + "id": "postgresql-socket-inactivity-timeout" + } + ], + "kind": "page" + }, + { + "id": "477-release-notes/1.9#installation", + "title": "Zero 1.9", + "searchTitle": "Installation", + "sectionTitle": "Installation", + "sectionId": "installation", + "url": "/docs/release-notes/1.9", + "content": "npm install @rocicorp/zero@1.9 You can use zero-cache from Docker Hub or GHCR: docker pull rocicorp/zero:1.9.0 # or docker pull ghcr.io/rocicorp/zero:1.9.0", + "kind": "section" + }, + { + "id": "478-release-notes/1.9#overview", + "title": "Zero 1.9", + "searchTitle": "Overview", + "sectionTitle": "Overview", + "sectionId": "overview", + "url": "/docs/release-notes/1.9", + "content": "Zero 1.9 improves query correctness and zero-cache reliability.", + "kind": "section" + }, + { + "id": "479-release-notes/1.9#performance", + "title": "Zero 1.9", + "searchTitle": "Performance", + "sectionTitle": "Performance", + "sectionId": "performance", + "url": "/docs/release-notes/1.9", + "content": "Deferred.", + "kind": "section" + }, + { + "id": "480-release-notes/1.9#fixes", + "title": "Zero 1.9", + "searchTitle": "Fixes", + "sectionTitle": "Fixes", + "sectionId": "fixes", + "url": "/docs/release-notes/1.9", + "content": "Ordered queries now paginate and maintain windows correctly when cursor fields contain NULL, including compound tie-break fields and reverse walks. This prevents skipped rows, empty windows, and related Bound should be set failures. (thanks @YevheniiKotyrlo!) Schema construction, CRUD mutators, and materialized views now preserve a key named __proto__ as user data instead of invoking JavaScript's legacy prototype setter. (thanks @tjenkinson!) Clients now receive changed rows after a server-side query is rebuilt, instead of retaining stale results in a rare rehydration case. zero-cache now bounds its SQLite prepared-statement caches with LRU eviction, preventing unbounded statement retention when applications generate many distinct query shapes. Replication lag reports now retry when an expected report is missing, and serving-lag metrics exclude disconnected or not-yet-validated client groups. See the updated OpenTelemetry metric descriptions. zero-cache now detects and resets PostgreSQL connections that stop carrying wire traffic, including over TLS, allowing work to recover from proxy-created half-open sockets. See Breaking Changes. zero-cache now releases custom-query caches when client groups stop, preventing inactive groups from retaining timers and transformed queries.", + "kind": "section" + }, + { + "id": "481-release-notes/1.9#breaking-changes", + "title": "Zero 1.9", + "searchTitle": "Breaking Changes", + "sectionTitle": "Breaking Changes", + "sectionId": "breaking-changes", + "url": "/docs/release-notes/1.9", + "content": "PostgreSQL Socket Inactivity Timeout zero-cache now monitors wire activity on its PostgreSQL connections. By default, it checks every two minutes and resets a connection after one to two inactive intervals. This recovers half-open connections, but can interrupt a long-running statement that legitimately produces no network traffic. If legitimate Postgres operations can remain silent for this long, set ZERO_PG_SOCKET_INACTIVITY_TIMEOUT on zero-cache to a longer interval in milliseconds. Set it to 0 to disable the watchdog.", + "kind": "section" + }, + { + "id": "482-release-notes/1.9#postgresql-socket-inactivity-timeout", + "title": "Zero 1.9", + "searchTitle": "PostgreSQL Socket Inactivity Timeout", + "sectionTitle": "PostgreSQL Socket Inactivity Timeout", + "sectionId": "postgresql-socket-inactivity-timeout", + "url": "/docs/release-notes/1.9", + "content": "zero-cache now monitors wire activity on its PostgreSQL connections. By default, it checks every two minutes and resets a connection after one to two inactive intervals. This recovers half-open connections, but can interrupt a long-running statement that legitimately produces no network traffic. If legitimate Postgres operations can remain silent for this long, set ZERO_PG_SOCKET_INACTIVITY_TIMEOUT on zero-cache to a longer interval in milliseconds. Set it to 0 to disable the watchdog.", + "kind": "section" + }, + { + "id": "62-release-notes", "title": "Release Notes", "searchTitle": "Release Notes", "url": "/docs/release-notes", - "content": "Zero 1.8: Observability and Reliability Zero 1.7: Query Correctness and Performance Zero 1.6: PlanetScale Failover Support Zero 1.5: Schema Change Improvements and Client Group Auth Zero 1.4: Performance and Reliability Improvements Zero 1.3: Faster Initial Sync and Other Perf Improvements Zero 1.2: IVM Performance and Bug Fixes Zero 1.1: Replication Monitoring Zero 1.0: First Stable Release Zero 0.26: Schema Backfill and Scalar Subqueries Zero 0.25: DX Overhaul, Query Planning Zero 0.24: Join Flipping, Cookie Auth, Inspector Updates Zero 0.23: Synced Queries and React Native Support Zero 0.22: Simplified TTLs Zero 0.21: PG arrays, TanStack starter, and more Zero 0.20: Full Supabase support, performance improvements Zero 0.19: Many, many bugfixes and cleanups Zero 0.18: Custom Mutators Zero 0.17: Background Queries Zero 0.16: Lambda-Based Permission Deployment Zero 0.15: Live Permission Updates Zero 0.14: Name Mapping and Multischema Zero 0.13: Multinode and SST Zero 0.12: Circular Relationships Zero 0.11: Windows Zero 0.10: Remove Top-Level Await Zero 0.9: JWK Support Zero 0.8: Schema Autobuild, Result Types, and Enums Zero 0.7: Read Perms and Docker Zero 0.6: Relationship Filters Zero 0.5: JSON Columns Zero 0.4: Compound Filters Zero 0.3: Schema Migrations and Write Perms Zero 0.2: Skip Mode and Computed PKs Zero 0.1: First Release", + "content": "Zero 1.9: Query Correctness and Reliability Zero 1.8: Observability and Reliability Zero 1.7: Query Correctness and Performance Zero 1.6: PlanetScale Failover Support Zero 1.5: Schema Change Improvements and Client Group Auth Zero 1.4: Performance and Reliability Improvements Zero 1.3: Faster Initial Sync and Other Perf Improvements Zero 1.2: IVM Performance and Bug Fixes Zero 1.1: Replication Monitoring Zero 1.0: First Stable Release Zero 0.26: Schema Backfill and Scalar Subqueries Zero 0.25: DX Overhaul, Query Planning Zero 0.24: Join Flipping, Cookie Auth, Inspector Updates Zero 0.23: Synced Queries and React Native Support Zero 0.22: Simplified TTLs Zero 0.21: PG arrays, TanStack starter, and more Zero 0.20: Full Supabase support, performance improvements Zero 0.19: Many, many bugfixes and cleanups Zero 0.18: Custom Mutators Zero 0.17: Background Queries Zero 0.16: Lambda-Based Permission Deployment Zero 0.15: Live Permission Updates Zero 0.14: Name Mapping and Multischema Zero 0.13: Multinode and SST Zero 0.12: Circular Relationships Zero 0.11: Windows Zero 0.10: Remove Top-Level Await Zero 0.9: JWK Support Zero 0.8: Schema Autobuild, Result Types, and Enums Zero 0.7: Read Perms and Docker Zero 0.6: Relationship Filters Zero 0.5: JSON Columns Zero 0.4: Compound Filters Zero 0.3: Schema Migrations and Write Perms Zero 0.2: Skip Mode and Computed PKs Zero 0.1: First Release", "headings": [], "kind": "page" }, { - "id": "62-reporting-bugs", + "id": "63-reporting-bugs", "title": "Reporting Bugs", "searchTitle": "Reporting Bugs", "url": "/docs/reporting-bugs", @@ -6218,7 +6326,7 @@ "kind": "page" }, { - "id": "475-reporting-bugs#zbugs", + "id": "483-reporting-bugs#zbugs", "title": "Reporting Bugs", "searchTitle": "zbugs", "sectionTitle": "zbugs", @@ -6228,7 +6336,7 @@ "kind": "section" }, { - "id": "476-reporting-bugs#discord", + "id": "484-reporting-bugs#discord", "title": "Reporting Bugs", "searchTitle": "Discord", "sectionTitle": "Discord", @@ -6238,7 +6346,7 @@ "kind": "section" }, { - "id": "63-rest", + "id": "64-rest", "title": "REST", "searchTitle": "REST", "url": "/docs/rest", @@ -6264,7 +6372,7 @@ "kind": "page" }, { - "id": "477-rest#pattern", + "id": "485-rest#pattern", "title": "REST", "searchTitle": "Pattern", "sectionTitle": "Pattern", @@ -6274,7 +6382,7 @@ "kind": "section" }, { - "id": "478-rest#tanstack-start-example", + "id": "486-rest#tanstack-start-example", "title": "REST", "searchTitle": "TanStack Start Example", "sectionTitle": "TanStack Start Example", @@ -6284,7 +6392,7 @@ "kind": "section" }, { - "id": "479-rest#openapi-generation", + "id": "487-rest#openapi-generation", "title": "REST", "searchTitle": "OpenAPI Generation", "sectionTitle": "OpenAPI Generation", @@ -6294,7 +6402,7 @@ "kind": "section" }, { - "id": "480-rest#full-working-example", + "id": "488-rest#full-working-example", "title": "REST", "searchTitle": "Full Working Example", "sectionTitle": "Full Working Example", @@ -6304,7 +6412,7 @@ "kind": "section" }, { - "id": "64-roadmap", + "id": "65-roadmap", "title": "Roadmap", "searchTitle": "Roadmap", "url": "/docs/roadmap", @@ -6322,7 +6430,7 @@ "kind": "page" }, { - "id": "481-roadmap#q4-2025", + "id": "489-roadmap#q4-2025", "title": "Roadmap", "searchTitle": "Q4 2025", "sectionTitle": "Q4 2025", @@ -6332,7 +6440,7 @@ "kind": "section" }, { - "id": "482-roadmap#beyond", + "id": "490-roadmap#beyond", "title": "Roadmap", "searchTitle": "Beyond", "sectionTitle": "Beyond", @@ -6342,7 +6450,7 @@ "kind": "section" }, { - "id": "65-samples", + "id": "66-samples", "title": "Samples", "searchTitle": "Samples", "url": "/docs/samples", @@ -6368,7 +6476,7 @@ "kind": "page" }, { - "id": "483-samples#gigabugs", + "id": "491-samples#gigabugs", "title": "Samples", "searchTitle": "Gigabugs", "sectionTitle": "Gigabugs", @@ -6378,7 +6486,7 @@ "kind": "section" }, { - "id": "484-samples#ztunes", + "id": "492-samples#ztunes", "title": "Samples", "searchTitle": "ztunes", "sectionTitle": "ztunes", @@ -6388,7 +6496,7 @@ "kind": "section" }, { - "id": "485-samples#zslack", + "id": "493-samples#zslack", "title": "Samples", "searchTitle": "zslack", "sectionTitle": "zslack", @@ -6398,7 +6506,7 @@ "kind": "section" }, { - "id": "486-samples#zero-music", + "id": "494-samples#zero-music", "title": "Samples", "searchTitle": "zero-music", "sectionTitle": "zero-music", @@ -6408,7 +6516,7 @@ "kind": "section" }, { - "id": "66-schema", + "id": "67-schema", "title": "Zero Schema", "searchTitle": "Zero Schema", "url": "/docs/schema", @@ -6534,7 +6642,7 @@ "kind": "page" }, { - "id": "487-schema#generating-from-database", + "id": "495-schema#generating-from-database", "title": "Zero Schema", "searchTitle": "Generating from Database", "sectionTitle": "Generating from Database", @@ -6544,7 +6652,7 @@ "kind": "section" }, { - "id": "488-schema#writing-by-hand", + "id": "496-schema#writing-by-hand", "title": "Zero Schema", "searchTitle": "Writing by Hand", "sectionTitle": "Writing by Hand", @@ -6554,7 +6662,7 @@ "kind": "section" }, { - "id": "489-schema#table-schemas", + "id": "497-schema#table-schemas", "title": "Zero Schema", "searchTitle": "Table Schemas", "sectionTitle": "Table Schemas", @@ -6564,7 +6672,7 @@ "kind": "section" }, { - "id": "490-schema#name-mapping", + "id": "498-schema#name-mapping", "title": "Zero Schema", "searchTitle": "Name Mapping", "sectionTitle": "Name Mapping", @@ -6574,7 +6682,7 @@ "kind": "section" }, { - "id": "491-schema#multiple-schemas", + "id": "499-schema#multiple-schemas", "title": "Zero Schema", "searchTitle": "Multiple Schemas", "sectionTitle": "Multiple Schemas", @@ -6584,7 +6692,7 @@ "kind": "section" }, { - "id": "492-schema#optional-columns", + "id": "500-schema#optional-columns", "title": "Zero Schema", "searchTitle": "Optional Columns", "sectionTitle": "Optional Columns", @@ -6594,7 +6702,7 @@ "kind": "section" }, { - "id": "493-schema#enumerations", + "id": "501-schema#enumerations", "title": "Zero Schema", "searchTitle": "Enumerations", "sectionTitle": "Enumerations", @@ -6604,7 +6712,7 @@ "kind": "section" }, { - "id": "494-schema#custom-json-types", + "id": "502-schema#custom-json-types", "title": "Zero Schema", "searchTitle": "Custom JSON Types", "sectionTitle": "Custom JSON Types", @@ -6614,7 +6722,7 @@ "kind": "section" }, { - "id": "495-schema#compound-primary-keys", + "id": "503-schema#compound-primary-keys", "title": "Zero Schema", "searchTitle": "Compound Primary Keys", "sectionTitle": "Compound Primary Keys", @@ -6624,7 +6732,7 @@ "kind": "section" }, { - "id": "496-schema#relationships", + "id": "504-schema#relationships", "title": "Zero Schema", "searchTitle": "Relationships", "sectionTitle": "Relationships", @@ -6634,7 +6742,7 @@ "kind": "section" }, { - "id": "497-schema#many-to-many-relationships", + "id": "505-schema#many-to-many-relationships", "title": "Zero Schema", "searchTitle": "Many-to-Many Relationships", "sectionTitle": "Many-to-Many Relationships", @@ -6644,7 +6752,7 @@ "kind": "section" }, { - "id": "498-schema#compound-keys-relationships", + "id": "506-schema#compound-keys-relationships", "title": "Zero Schema", "searchTitle": "Compound Keys Relationships", "sectionTitle": "Compound Keys Relationships", @@ -6654,7 +6762,7 @@ "kind": "section" }, { - "id": "499-schema#circular-relationships", + "id": "507-schema#circular-relationships", "title": "Zero Schema", "searchTitle": "Circular Relationships", "sectionTitle": "Circular Relationships", @@ -6664,7 +6772,7 @@ "kind": "section" }, { - "id": "500-schema#database-schemas", + "id": "508-schema#database-schemas", "title": "Zero Schema", "searchTitle": "Database Schemas", "sectionTitle": "Database Schemas", @@ -6674,7 +6782,7 @@ "kind": "section" }, { - "id": "501-schema#register-schema-type", + "id": "509-schema#register-schema-type", "title": "Zero Schema", "searchTitle": "Register Schema Type", "sectionTitle": "Register Schema Type", @@ -6684,7 +6792,7 @@ "kind": "section" }, { - "id": "502-schema#schema-changes", + "id": "510-schema#schema-changes", "title": "Zero Schema", "searchTitle": "Schema Changes", "sectionTitle": "Schema Changes", @@ -6694,7 +6802,7 @@ "kind": "section" }, { - "id": "503-schema#development", + "id": "511-schema#development", "title": "Zero Schema", "searchTitle": "Development", "sectionTitle": "Development", @@ -6704,7 +6812,7 @@ "kind": "section" }, { - "id": "504-schema#production", + "id": "512-schema#production", "title": "Zero Schema", "searchTitle": "Production", "sectionTitle": "Production", @@ -6714,7 +6822,7 @@ "kind": "section" }, { - "id": "505-schema#expand-changes", + "id": "513-schema#expand-changes", "title": "Zero Schema", "searchTitle": "Expand Changes", "sectionTitle": "Expand Changes", @@ -6724,7 +6832,7 @@ "kind": "section" }, { - "id": "506-schema#contract-changes", + "id": "514-schema#contract-changes", "title": "Zero Schema", "searchTitle": "Contract Changes", "sectionTitle": "Contract Changes", @@ -6734,7 +6842,7 @@ "kind": "section" }, { - "id": "507-schema#compound-changes", + "id": "515-schema#compound-changes", "title": "Zero Schema", "searchTitle": "Compound Changes", "sectionTitle": "Compound Changes", @@ -6744,7 +6852,7 @@ "kind": "section" }, { - "id": "508-schema#examples", + "id": "516-schema#examples", "title": "Zero Schema", "searchTitle": "Examples", "sectionTitle": "Examples", @@ -6754,7 +6862,7 @@ "kind": "section" }, { - "id": "509-schema#adding-a-column", + "id": "517-schema#adding-a-column", "title": "Zero Schema", "searchTitle": "Adding a Column", "sectionTitle": "Adding a Column", @@ -6764,7 +6872,7 @@ "kind": "section" }, { - "id": "510-schema#removing-a-column", + "id": "518-schema#removing-a-column", "title": "Zero Schema", "searchTitle": "Removing a Column", "sectionTitle": "Removing a Column", @@ -6774,7 +6882,7 @@ "kind": "section" }, { - "id": "511-schema#renaming-a-column", + "id": "519-schema#renaming-a-column", "title": "Zero Schema", "searchTitle": "Renaming a Column", "sectionTitle": "Renaming a Column", @@ -6784,7 +6892,7 @@ "kind": "section" }, { - "id": "512-schema#making-a-column-optional", + "id": "520-schema#making-a-column-optional", "title": "Zero Schema", "searchTitle": "Making a Column Optional", "sectionTitle": "Making a Column Optional", @@ -6794,7 +6902,7 @@ "kind": "section" }, { - "id": "513-schema#quick-reference", + "id": "521-schema#quick-reference", "title": "Zero Schema", "searchTitle": "Quick Reference", "sectionTitle": "Quick Reference", @@ -6804,7 +6912,7 @@ "kind": "section" }, { - "id": "514-schema#backfill", + "id": "522-schema#backfill", "title": "Zero Schema", "searchTitle": "Backfill", "sectionTitle": "Backfill", @@ -6814,7 +6922,7 @@ "kind": "section" }, { - "id": "515-schema#monitoring-backfill-progress", + "id": "523-schema#monitoring-backfill-progress", "title": "Zero Schema", "searchTitle": "Monitoring Backfill Progress", "sectionTitle": "Monitoring Backfill Progress", @@ -6824,7 +6932,7 @@ "kind": "section" }, { - "id": "67-self-host", + "id": "68-self-host", "title": "Self-Hosting Zero", "searchTitle": "Self-Hosting Zero", "url": "/docs/self-host", @@ -6886,7 +6994,7 @@ "kind": "page" }, { - "id": "516-self-host#docker-images", + "id": "524-self-host#docker-images", "title": "Self-Hosting Zero", "searchTitle": "Docker Images", "sectionTitle": "Docker Images", @@ -6896,7 +7004,7 @@ "kind": "section" }, { - "id": "517-self-host#minimum-viable-strategy", + "id": "525-self-host#minimum-viable-strategy", "title": "Self-Hosting Zero", "searchTitle": "Minimum Viable Strategy", "sectionTitle": "Minimum Viable Strategy", @@ -6906,7 +7014,7 @@ "kind": "section" }, { - "id": "518-self-host#maximal-strategy", + "id": "526-self-host#maximal-strategy", "title": "Self-Hosting Zero", "searchTitle": "Maximal Strategy", "sectionTitle": "Maximal Strategy", @@ -6916,7 +7024,7 @@ "kind": "section" }, { - "id": "519-self-host#replica-lifecycle", + "id": "527-self-host#replica-lifecycle", "title": "Self-Hosting Zero", "searchTitle": "Replica Lifecycle", "sectionTitle": "Replica Lifecycle", @@ -6926,7 +7034,7 @@ "kind": "section" }, { - "id": "520-self-host#performance", + "id": "528-self-host#performance", "title": "Self-Hosting Zero", "searchTitle": "Performance", "sectionTitle": "Performance", @@ -6936,7 +7044,7 @@ "kind": "section" }, { - "id": "521-self-host#hydration", + "id": "529-self-host#hydration", "title": "Self-Hosting Zero", "searchTitle": "Hydration", "sectionTitle": "Hydration", @@ -6946,7 +7054,7 @@ "kind": "section" }, { - "id": "522-self-host#ivm-advancement", + "id": "530-self-host#ivm-advancement", "title": "Self-Hosting Zero", "searchTitle": "IVM advancement", "sectionTitle": "IVM advancement", @@ -6956,7 +7064,7 @@ "kind": "section" }, { - "id": "523-self-host#system-level", + "id": "531-self-host#system-level", "title": "Self-Hosting Zero", "searchTitle": "System-level", "sectionTitle": "System-level", @@ -6966,7 +7074,7 @@ "kind": "section" }, { - "id": "524-self-host#networking", + "id": "532-self-host#networking", "title": "Self-Hosting Zero", "searchTitle": "Networking", "sectionTitle": "Networking", @@ -6976,7 +7084,7 @@ "kind": "section" }, { - "id": "525-self-host#sticky-sessions", + "id": "533-self-host#sticky-sessions", "title": "Self-Hosting Zero", "searchTitle": "Sticky Sessions", "sectionTitle": "Sticky Sessions", @@ -6986,7 +7094,7 @@ "kind": "section" }, { - "id": "526-self-host#rolling-updates", + "id": "534-self-host#rolling-updates", "title": "Self-Hosting Zero", "searchTitle": "Rolling Updates", "sectionTitle": "Rolling Updates", @@ -6996,7 +7104,7 @@ "kind": "section" }, { - "id": "527-self-host#clientserver-version-compatibility", + "id": "535-self-host#clientserver-version-compatibility", "title": "Self-Hosting Zero", "searchTitle": "Client/Server Version Compatibility", "sectionTitle": "Client/Server Version Compatibility", @@ -7006,7 +7114,7 @@ "kind": "section" }, { - "id": "528-self-host#configuration", + "id": "536-self-host#configuration", "title": "Self-Hosting Zero", "searchTitle": "Configuration", "sectionTitle": "Configuration", @@ -7016,7 +7124,7 @@ "kind": "section" }, { - "id": "68-server-zql", + "id": "69-server-zql", "title": "ZQL on the Server", "searchTitle": "ZQL on the Server", "url": "/docs/server-zql", @@ -7042,7 +7150,7 @@ "kind": "page" }, { - "id": "529-server-zql#creating-a-database", + "id": "537-server-zql#creating-a-database", "title": "ZQL on the Server", "searchTitle": "Creating a Database", "sectionTitle": "Creating a Database", @@ -7052,7 +7160,7 @@ "kind": "section" }, { - "id": "530-server-zql#custom-database", + "id": "538-server-zql#custom-database", "title": "ZQL on the Server", "searchTitle": "Custom Database", "sectionTitle": "Custom Database", @@ -7062,7 +7170,7 @@ "kind": "section" }, { - "id": "531-server-zql#running-zql", + "id": "539-server-zql#running-zql", "title": "ZQL on the Server", "searchTitle": "Running ZQL", "sectionTitle": "Running ZQL", @@ -7072,7 +7180,7 @@ "kind": "section" }, { - "id": "532-server-zql#ssr", + "id": "540-server-zql#ssr", "title": "ZQL on the Server", "searchTitle": "SSR", "sectionTitle": "SSR", @@ -7082,7 +7190,7 @@ "kind": "section" }, { - "id": "69-solidjs", + "id": "70-solidjs", "title": "SolidJS", "searchTitle": "SolidJS", "url": "/docs/solidjs", @@ -7104,7 +7212,7 @@ "kind": "page" }, { - "id": "533-solidjs#setup", + "id": "541-solidjs#setup", "title": "SolidJS", "searchTitle": "Setup", "sectionTitle": "Setup", @@ -7114,7 +7222,7 @@ "kind": "section" }, { - "id": "534-solidjs#usage", + "id": "542-solidjs#usage", "title": "SolidJS", "searchTitle": "Usage", "sectionTitle": "Usage", @@ -7124,7 +7232,7 @@ "kind": "section" }, { - "id": "535-solidjs#examples", + "id": "543-solidjs#examples", "title": "SolidJS", "searchTitle": "Examples", "sectionTitle": "Examples", @@ -7134,7 +7242,7 @@ "kind": "section" }, { - "id": "70-status", + "id": "71-status", "title": "Project Status", "searchTitle": "Project Status", "url": "/docs/status", @@ -7160,7 +7268,7 @@ "kind": "page" }, { - "id": "536-status#breaking-changes", + "id": "544-status#breaking-changes", "title": "Project Status", "searchTitle": "Breaking Changes", "sectionTitle": "Breaking Changes", @@ -7170,7 +7278,7 @@ "kind": "section" }, { - "id": "537-status#roadmap", + "id": "545-status#roadmap", "title": "Project Status", "searchTitle": "Roadmap", "sectionTitle": "Roadmap", @@ -7180,7 +7288,7 @@ "kind": "section" }, { - "id": "538-status#2026", + "id": "546-status#2026", "title": "Project Status", "searchTitle": "2026", "sectionTitle": "2026", @@ -7190,7 +7298,7 @@ "kind": "section" }, { - "id": "539-status#soon", + "id": "547-status#soon", "title": "Project Status", "searchTitle": "Soon", "sectionTitle": "Soon", @@ -7200,7 +7308,7 @@ "kind": "section" }, { - "id": "71-sync", + "id": "72-sync", "title": "What is Sync?", "searchTitle": "What is Sync?", "url": "/docs/sync", @@ -7222,7 +7330,7 @@ "kind": "page" }, { - "id": "540-sync#problem", + "id": "548-sync#problem", "title": "What is Sync?", "searchTitle": "Problem", "sectionTitle": "Problem", @@ -7232,7 +7340,7 @@ "kind": "section" }, { - "id": "541-sync#solution", + "id": "549-sync#solution", "title": "What is Sync?", "searchTitle": "Solution", "sectionTitle": "Solution", @@ -7242,7 +7350,7 @@ "kind": "section" }, { - "id": "542-sync#history-of-sync", + "id": "550-sync#history-of-sync", "title": "What is Sync?", "searchTitle": "History of Sync", "sectionTitle": "History of Sync", @@ -7252,7 +7360,7 @@ "kind": "section" }, { - "id": "72-tutorial", + "id": "73-tutorial", "title": "Tutorial", "searchTitle": "Tutorial", "url": "/docs/tutorial", @@ -7326,7 +7434,7 @@ "kind": "page" }, { - "id": "543-tutorial#setup", + "id": "551-tutorial#setup", "title": "Tutorial", "searchTitle": "Setup", "sectionTitle": "Setup", @@ -7336,7 +7444,7 @@ "kind": "section" }, { - "id": "544-tutorial#create-a-project", + "id": "552-tutorial#create-a-project", "title": "Tutorial", "searchTitle": "Create a Project", "sectionTitle": "Create a Project", @@ -7346,7 +7454,7 @@ "kind": "section" }, { - "id": "545-tutorial#set-up-your-database", + "id": "553-tutorial#set-up-your-database", "title": "Tutorial", "searchTitle": "Set Up Your Database", "sectionTitle": "Set Up Your Database", @@ -7356,7 +7464,7 @@ "kind": "section" }, { - "id": "546-tutorial#install-and-run-zero-cache", + "id": "554-tutorial#install-and-run-zero-cache", "title": "Tutorial", "searchTitle": "Install and Run Zero-Cache", "sectionTitle": "Install and Run Zero-Cache", @@ -7366,7 +7474,7 @@ "kind": "section" }, { - "id": "547-tutorial#integrate-zero", + "id": "555-tutorial#integrate-zero", "title": "Tutorial", "searchTitle": "Integrate Zero", "sectionTitle": "Integrate Zero", @@ -7376,7 +7484,7 @@ "kind": "section" }, { - "id": "548-tutorial#set-up-your-zero-schema", + "id": "556-tutorial#set-up-your-zero-schema", "title": "Tutorial", "searchTitle": "Set Up Your Zero Schema", "sectionTitle": "Set Up Your Zero Schema", @@ -7386,7 +7494,7 @@ "kind": "section" }, { - "id": "549-tutorial#set-up-the-zero-client", + "id": "557-tutorial#set-up-the-zero-client", "title": "Tutorial", "searchTitle": "Set Up the Zero Client", "sectionTitle": "Set Up the Zero Client", @@ -7396,7 +7504,7 @@ "kind": "section" }, { - "id": "550-tutorial#sync-data", + "id": "558-tutorial#sync-data", "title": "Tutorial", "searchTitle": "Sync Data", "sectionTitle": "Sync Data", @@ -7406,7 +7514,7 @@ "kind": "section" }, { - "id": "551-tutorial#define-query", + "id": "559-tutorial#define-query", "title": "Tutorial", "searchTitle": "Define Query", "sectionTitle": "Define Query", @@ -7416,7 +7524,7 @@ "kind": "section" }, { - "id": "552-tutorial#add-query-endpoint", + "id": "560-tutorial#add-query-endpoint", "title": "Tutorial", "searchTitle": "Add Query Endpoint", "sectionTitle": "Add Query Endpoint", @@ -7426,7 +7534,7 @@ "kind": "section" }, { - "id": "553-tutorial#invoke-query", + "id": "561-tutorial#invoke-query", "title": "Tutorial", "searchTitle": "Invoke Query", "sectionTitle": "Invoke Query", @@ -7436,7 +7544,7 @@ "kind": "section" }, { - "id": "554-tutorial#mutate-data", + "id": "562-tutorial#mutate-data", "title": "Tutorial", "searchTitle": "Mutate Data", "sectionTitle": "Mutate Data", @@ -7446,7 +7554,7 @@ "kind": "section" }, { - "id": "555-tutorial#define-mutators", + "id": "563-tutorial#define-mutators", "title": "Tutorial", "searchTitle": "Define Mutators", "sectionTitle": "Define Mutators", @@ -7456,7 +7564,7 @@ "kind": "section" }, { - "id": "556-tutorial#add-mutate-endpoint", + "id": "564-tutorial#add-mutate-endpoint", "title": "Tutorial", "searchTitle": "Add Mutate Endpoint", "sectionTitle": "Add Mutate Endpoint", @@ -7466,7 +7574,7 @@ "kind": "section" }, { - "id": "557-tutorial#invoke-mutators", + "id": "565-tutorial#invoke-mutators", "title": "Tutorial", "searchTitle": "Invoke Mutators", "sectionTitle": "Invoke Mutators", @@ -7476,7 +7584,7 @@ "kind": "section" }, { - "id": "558-tutorial#next-steps", + "id": "566-tutorial#next-steps", "title": "Tutorial", "searchTitle": "Next Steps", "sectionTitle": "Next Steps", @@ -7486,7 +7594,7 @@ "kind": "section" }, { - "id": "73-when-to-use", + "id": "74-when-to-use", "title": "When To Use Zero", "searchTitle": "When To Use Zero", "url": "/docs/when-to-use", @@ -7552,7 +7660,7 @@ "kind": "page" }, { - "id": "559-when-to-use#zero-might-be-a-good-fit", + "id": "567-when-to-use#zero-might-be-a-good-fit", "title": "When To Use Zero", "searchTitle": "Zero Might be a Good Fit", "sectionTitle": "Zero Might be a Good Fit", @@ -7562,7 +7670,7 @@ "kind": "section" }, { - "id": "560-when-to-use#you-want-to-sync-only-a-small-subset-of-data-to-client", + "id": "568-when-to-use#you-want-to-sync-only-a-small-subset-of-data-to-client", "title": "When To Use Zero", "searchTitle": "You want to sync only a small subset of data to client", "sectionTitle": "You want to sync only a small subset of data to client", @@ -7572,7 +7680,7 @@ "kind": "section" }, { - "id": "561-when-to-use#you-need-fine-grained-read-or-write-permissions", + "id": "569-when-to-use#you-need-fine-grained-read-or-write-permissions", "title": "When To Use Zero", "searchTitle": "You need fine-grained read or write permissions", "sectionTitle": "You need fine-grained read or write permissions", @@ -7582,7 +7690,7 @@ "kind": "section" }, { - "id": "562-when-to-use#you-are-building-a-traditional-client-server-web-app", + "id": "570-when-to-use#you-are-building-a-traditional-client-server-web-app", "title": "When To Use Zero", "searchTitle": "You are building a traditional client-server web app", "sectionTitle": "You are building a traditional client-server web app", @@ -7592,7 +7700,7 @@ "kind": "section" }, { - "id": "563-when-to-use#you-use-postgresql", + "id": "571-when-to-use#you-use-postgresql", "title": "When To Use Zero", "searchTitle": "You use PostgreSQL", "sectionTitle": "You use PostgreSQL", @@ -7602,7 +7710,7 @@ "kind": "section" }, { - "id": "564-when-to-use#your-app-is-broadly-like-linear", + "id": "572-when-to-use#your-app-is-broadly-like-linear", "title": "When To Use Zero", "searchTitle": "Your app is broadly \"like Linear\"", "sectionTitle": "Your app is broadly \"like Linear\"", @@ -7612,7 +7720,7 @@ "kind": "section" }, { - "id": "565-when-to-use#interaction-performance-is-very-important-to-you", + "id": "573-when-to-use#interaction-performance-is-very-important-to-you", "title": "When To Use Zero", "searchTitle": "Interaction performance is very important to you", "sectionTitle": "Interaction performance is very important to you", @@ -7622,7 +7730,7 @@ "kind": "section" }, { - "id": "566-when-to-use#zero-might-not-be-a-good-fit", + "id": "574-when-to-use#zero-might-not-be-a-good-fit", "title": "When To Use Zero", "searchTitle": "Zero Might Not be a Good Fit", "sectionTitle": "Zero Might Not be a Good Fit", @@ -7632,7 +7740,7 @@ "kind": "section" }, { - "id": "567-when-to-use#you-need-the-privacy-or-data-ownership-benefits-of-local-first", + "id": "575-when-to-use#you-need-the-privacy-or-data-ownership-benefits-of-local-first", "title": "When To Use Zero", "searchTitle": "You need the privacy or data ownership benefits of local-first", "sectionTitle": "You need the privacy or data ownership benefits of local-first", @@ -7642,7 +7750,7 @@ "kind": "section" }, { - "id": "568-when-to-use#you-need-to-support-offline-writes-or-long-periods-offline", + "id": "576-when-to-use#you-need-to-support-offline-writes-or-long-periods-offline", "title": "When To Use Zero", "searchTitle": "You need to support offline writes or long periods offline", "sectionTitle": "You need to support offline writes or long periods offline", @@ -7652,7 +7760,7 @@ "kind": "section" }, { - "id": "569-when-to-use#you-are-building-a-native-mobile-app", + "id": "577-when-to-use#you-are-building-a-native-mobile-app", "title": "When To Use Zero", "searchTitle": "You are building a native mobile app", "sectionTitle": "You are building a native mobile app", @@ -7662,7 +7770,7 @@ "kind": "section" }, { - "id": "570-when-to-use#the-total-backend-dataset-is--100gb", + "id": "578-when-to-use#the-total-backend-dataset-is--100gb", "title": "When To Use Zero", "searchTitle": "The total backend dataset is > ~100GB", "sectionTitle": "The total backend dataset is > ~100GB", @@ -7672,7 +7780,7 @@ "kind": "section" }, { - "id": "571-when-to-use#zero-might-not-be-a-good-fit-yet", + "id": "579-when-to-use#zero-might-not-be-a-good-fit-yet", "title": "When To Use Zero", "searchTitle": "Zero Might Not be a Good Fit Yet", "sectionTitle": "Zero Might Not be a Good Fit Yet", @@ -7682,7 +7790,7 @@ "kind": "section" }, { - "id": "572-when-to-use#alternatives", + "id": "580-when-to-use#alternatives", "title": "When To Use Zero", "searchTitle": "Alternatives", "sectionTitle": "Alternatives", @@ -7692,11 +7800,11 @@ "kind": "section" }, { - "id": "74-zero-cache-config", + "id": "75-zero-cache-config", "title": "zero-cache Config", "searchTitle": "zero-cache Config", "url": "/docs/zero-cache-config", - "content": "zero-cache is configured either via CLI flag or environment variable. There is no separate zero.config file. You can also see all available flags by running zero-cache --help. Required Flags Upstream DB The \"upstream\" authoritative postgres database. In the future we will support other types of upstream besides PG. flag: --upstream-db env: ZERO_UPSTREAM_DB required: true Admin Password A password used to administer zero-cache server, for example to access the /statz endpoint and the inspector. This is required in production (when NODE_ENV=production) because we want all Zero servers to be debuggable using admin tools by default, without needing a restart. But we also don't want to expose sensitive data using them. flag: --admin-password env: ZERO_ADMIN_PASSWORD required: in production (when NODE_ENV=production) Optional Flags App ID Unique identifier for the app. Multiple zero-cache apps can run on a single upstream database, each of which is isolated from the others, with its own permissions, sharding (future feature), and change/cvr databases. The metadata of an app is stored in an upstream schema with the same name, e.g. zero, and the metadata for each app shard, e.g. client and mutation ids, is stored in the {app-id}_{#} schema. (Currently there is only a single \"0\" shard, but this will change with sharding). The CVR and Change data are managed in schemas named {app-id}_{shard-num}/cvr and {app-id}_{shard-num}/cdc, respectively, allowing multiple apps and shards to share the same database instance (e.g. a Postgres \"cluster\") for CVR and Change management. Due to constraints on replication slot names, an App ID may only consist of lower-case letters, numbers, and the underscore character. Note that this option is used by both zero-cache and zero-deploy-permissions. flag: --app-id env: ZERO_APP_ID default: zero App Publications Postgres PUBLICATIONs that define the tables and columns to replicate. Publication names may not begin with an underscore, as zero reserves that prefix for internal use. If unspecified, zero-cache will create and use an internal publication that publishes all tables in the public schema, i.e.: CREATE PUBLICATION _{app-id}_public_0 FOR TABLES IN SCHEMA public; Note that changing the set of publications will result in resyncing the replica, which may involve downtime (replication lag) while the new replica is initializing. To change the set of publications without disrupting an existing app, a new app should be created. To use a custom publication, you can create one with: CREATE PUBLICATION zero_data FOR TABLES IN SCHEMA public; -- or, more selectively: CREATE PUBLICATION zero_data FOR TABLE users, orders; Then set the flag to that publication name, e.g.: ZERO_APP_PUBLICATIONS=zero_data. To specify multiple publications, separate them with commas, e.g.: ZERO_APP_PUBLICATIONS=zero_data1,zero_data2. flag: --app-publications env: ZERO_APP_PUBLICATIONS default: _{app-id}_public_0 Auth Revalidate Interval Seconds How often zero-cache re-checks that each live connection is still authorized to use your /query endpoint. On each interval, zero-cache sends a lightweight validation request using that connection's current auth context, such as forwarded cookies or an opaque auth token. If your query endpoint rejects that auth with a 401/403, the connection is disconnected. Use this to bound how long already-open connections can continue after logout, session expiry, token revocation, or other server-side auth changes that happen without a reconnect. Lower values enforce auth changes faster, but send more validation requests to /query. flag: --auth-revalidate-interval-seconds env: ZERO_AUTH_REVALIDATE_INTERVAL_SECONDS default: unset Auth Retransform Interval Seconds How often zero-cache refreshes a client group's synced or named query transformations using one validated connection from that group. This re-runs auth-sensitive query expansion even when the query set itself has not changed. It is useful when your query endpoint generates different ZQL based on current auth or server-side session state, such as roles, organization membership, feature flags, or other permissions-derived context. Use this to bound how long a client group can keep using stale auth-derived query shapes after backend auth state changes. Lower values pick up those changes faster, but do more /query transform work. If clients already call updateAuth whenever auth changes, this mainly serves as a background safety net for out-of-band auth changes. flag: --auth-retransform-interval-seconds env: ZERO_AUTH_RETRANSFORM_INTERVAL_SECONDS default: unset Auto Reset Automatically wipe and resync the replica when replication is halted. This situation can occur for configurations in which the upstream database provider prohibits event trigger creation, preventing the zero-cache from being able to correctly replicate schema changes. For such configurations, an upstream schema change will instead result in halting replication with an error indicating that the replica needs to be reset. When auto-reset is enabled, zero-cache will respond to such situations by shutting down, and when restarted, resetting the replica and all synced clients. This is a heavy-weight operation and can result in user-visible slowness or downtime if compute resources are scarce. flag: --auto-reset env: ZERO_AUTO_RESET default: true Change DB The Postgres database used to store recent replication log entries, in order to sync multiple view-syncers without requiring multiple replication slots on the upstream database. If unspecified, the upstream-db will be used. flag: --change-db env: ZERO_CHANGE_DB Change Max Connections The maximum number of connections to open to the change database. This is used by the change-streamer for catching up zero-cache replication subscriptions. flag: --change-max-conns env: ZERO_CHANGE_MAX_CONNS default: 5 Change Streamer Back Pressure Limit Heap Proportion The percentage of --max-old-space-size to use as a buffer for absorbing replication stream spikes. When the estimated amount of queued data exceeds this threshold, back pressure is applied to the replication stream, delaying downstream sync as a result. The threshold was determined empirically with load testing. Higher thresholds have resulted in OOMs. Note also that the byte-counting logic in the queue is strictly an underestimate of actual memory usage (but importantly, proportionally correct), so the queue is actually using more than what this proportion suggests. This parameter is exported as an emergency knob to reduce the size of the buffer in the event that the server OOMs from back pressure. Resist the urge to increase this proportion, as it is mainly useful for absorbing periodic spikes and does not meaningfully affect steady-state replication throughput; the latter is determined by other factors such as object serialization and PG throughput. In other words, the back pressure limit does not constrain replication throughput; rather, it protects the system when the upstream throughput exceeds the downstream throughput. flag: --change-streamer-back-pressure-limit-heap-proportion env: ZERO_CHANGE_STREAMER_BACK_PRESSURE_LIMIT_HEAP_PROPORTION default: 0.04 Change Streamer Flow Control Consensus Padding Seconds During periodic flow control checks (every 64kb), this is the amount of time to wait after the majority of subscribers have acked, after which replication continues even if some subscribers have yet to ack. This is not a timeout for the entire send; it starts only after the majority of receivers have acked. This allows a bounded amount of time for backlogged subscribers to catch up on each flush without forcing all subscribers to wait for the entire backlog to be processed. It is also useful for mitigating the effect of unresponsive subscribers due to severed WebSocket connections until liveness checks disconnect them. Set this to a negative number to disable early flow control releases. flag: --change-streamer-flow-control-consensus-padding-seconds env: ZERO_CHANGE_STREAMER_FLOW_CONTROL_CONSENSUS_PADDING_SECONDS default: 1 Change Streamer Mode The mode for running or connecting to the change-streamer: dedicated: runs the change-streamer and shuts down when another change-streamer takes over the replication slot. This is appropriate in a single-node configuration, or for the replication-manager in a multi-node configuration. discover: connects to the change-streamer as internally advertised in the change-db. This is appropriate for the view-syncers in a multi-node setup. This may not work in all networking configurations (e.g., some private networking or port forwarding setups). Using ZERO_CHANGE_STREAMER_URI with an explicit routable hostname is recommended instead. This option is ignored if ZERO_CHANGE_STREAMER_URI is set. flag: --change-streamer-mode env: ZERO_CHANGE_STREAMER_MODE default: dedicated Change Streamer Port The port on which the change-streamer runs. This is an internal protocol between the replication-manager and view-syncers, which runs in the same process tree in local development or a single-node configuration. If unspecified, defaults to --port + 1. flag: --change-streamer-port env: ZERO_CHANGE_STREAMER_PORT default: --port + 1 Change Streamer Startup Delay (ms) The delay to wait before the change-streamer takes over the replication stream (i.e. the handoff during replication-manager updates), to allow load balancers to register the task as healthy based on healthcheck parameters. If a change stream request is received during this interval, the delay will be canceled and the takeover will happen immediately, since the incoming request indicates that the task is registered as a target. flag: --change-streamer-startup-delay-ms env: ZERO_CHANGE_STREAMER_STARTUP_DELAY_MS default: 15000 Change Streamer URI When set, connects to the change-streamer at the given URI. In a multi-node setup, this should be specified in view-syncer options, pointing to the replication-manager URI, which runs a change-streamer on port 4849. flag: --change-streamer-uri env: ZERO_CHANGE_STREAMER_URI CVR DB The Postgres database used to store CVRs. CVRs (client view records) keep track of the data synced to clients in order to determine the diff to send on reconnect. If unspecified, the upstream-db will be used. flag: --cvr-db env: ZERO_CVR_DB CVR Garbage Collection Inactivity Threshold Hours The duration after which an inactive CVR is eligible for garbage collection. Garbage collection is incremental and periodic, so eligible CVRs are not necessarily purged immediately. flag: --cvr-garbage-collection-inactivity-threshold-hours env: ZERO_CVR_GARBAGE_COLLECTION_INACTIVITY_THRESHOLD_HOURS default: 48 CVR Garbage Collection Initial Batch Size The initial number of CVRs to purge per garbage collection interval. This number is increased linearly if the rate of new CVRs exceeds the rate of purged CVRs, in order to reach a steady state. Setting this to 0 effectively disables CVR garbage collection. flag: --cvr-garbage-collection-initial-batch-size env: ZERO_CVR_GARBAGE_COLLECTION_INITIAL_BATCH_SIZE default: 25 CVR Garbage Collection Initial Interval Seconds The initial interval at which to check and garbage collect inactive CVRs. This interval is increased exponentially (up to 16 minutes) when there is nothing to purge. flag: --cvr-garbage-collection-initial-interval-seconds env: ZERO_CVR_GARBAGE_COLLECTION_INITIAL_INTERVAL_SECONDS default: 60 CVR Max Connections The maximum number of connections to open to the CVR database. This is divided evenly amongst sync workers. Note that this number must allow for at least one connection per sync worker, or zero-cache will fail to start. See num-sync-workers. flag: --cvr-max-conns env: ZERO_CVR_MAX_CONNS default: 30 Enable Query Planner Enable the query planner for optimizing ZQL queries. The query planner analyzes and optimizes query execution by determining the most efficient join strategies. You can disable the planner if it is picking bad strategies. flag: --enable-query-planner env: ZERO_ENABLE_QUERY_PLANNER default: true Enable CRUD Mutations Enables support for legacy CRUD mutations. When this is false, view-syncers do not connect to the upstream database for CRUD writes, and push messages with CRUD mutations return an error response. flag: --enable-crud-mutations env: ZERO_ENABLE_CRUD_MUTATIONS default: true Enable Telemetry Zero collects anonymous telemetry data to help us understand usage. We collect: Zero version Uptime General machine information, like the number of CPUs, OS, CI/CD environment, etc. Information about usage, such as number of queries or mutations processed per hour. This is completely optional and can be disabled at any time. You can also opt-out by setting DO_NOT_TRACK=1. flag: --enable-telemetry env: ZERO_ENABLE_TELEMETRY default: true Initial Sync Table Copy Workers The number of parallel workers used to copy tables during initial sync. Each worker uses a database connection, copies a single table at a time, and buffers up to (approximately) 10 MB of table data in memory during initial sync. Increasing the number of workers may improve initial sync speed; however, local disk throughput (IOPS), upstream CPU, and network bandwidth may also be bottlenecks. flag: --initial-sync-table-copy-workers env: ZERO_INITIAL_SYNC_TABLE_COPY_WORKERS default: 5 Lazy Startup Delay starting the majority of zero-cache until first request. This is mainly intended to avoid connecting to Postgres replication stream until the first request is received, which can be useful i.e., for preview instances. Currently only supported in single-node mode. flag: --lazy-startup env: ZERO_LAZY_STARTUP default: false Litestream Backup URL The location of the litestream backup, usually an s3:// URL. This is only consulted by the replication-manager. view-syncers receive this information from the replication-manager. In multi-node deployments, this is required on the replication-manager so view-syncers can reserve snapshots; in single-node deployments it is optional. flag: --litestream-backup-url env: ZERO_LITESTREAM_BACKUP_URL Litestream Endpoint The S3-compatible endpoint URL to use for the litestream backup. This is only required for non-AWS services. The replication-manager and view-syncers must have the same endpoint. For example, to use Cloudflare R2: https://.r2.cloudflarestorage.com. flag: --litestream-endpoint env: ZERO_LITESTREAM_ENDPOINT Litestream Checkpoint Threshold MB The size of the WAL file at which to perform an SQlite checkpoint to apply the writes in the WAL to the main database file. Each checkpoint creates a new WAL segment file that will be backed up by litestream. Smaller thresholds may improve read performance, at the expense of creating more files to download when restoring the replica from the backup. flag: --litestream-checkpoint-threshold-mb env: ZERO_LITESTREAM_CHECKPOINT_THRESHOLD_MB default: 40 Litestream Config Path Path to the litestream yaml config file. zero-cache will run this with its environment variables, which can be referenced in the file via ${ENV} substitution, for example: ZERO_REPLICA_FILE for the db Path ZERO_LITESTREAM_BACKUP_LOCATION for the db replica url ZERO_LITESTREAM_LOG_LEVEL for the log Level ZERO_LOG_FORMAT for the log type flag: --litestream-config-path env: ZERO_LITESTREAM_CONFIG_PATH default: ./src/services/litestream/config.yml Litestream Executable Path to the litestream executable. This must be built from the rocicorp/litestream fork. This option has no effect if litestream-backup-url is unspecified. flag: --litestream-executable env: ZERO_LITESTREAM_EXECUTABLE Litestream Incremental Backup Interval Minutes The interval between incremental backups of the replica. Shorter intervals reduce the amount of change history that needs to be replayed when catching up a new view-syncer, at the expense of increasing the number of files needed to download for the initial litestream restore. flag: --litestream-incremental-backup-interval-minutes env: ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES default: 15 Litestream Maximum Checkpoint Page Count The WAL page count at which SQLite performs a RESTART checkpoint, which blocks writers until complete. Defaults to minCheckpointPageCount * 10. Set to 0 to disable RESTART checkpoints entirely. flag: --litestream-max-checkpoint-page-count env: ZERO_LITESTREAM_MAX_CHECKPOINT_PAGE_COUNT default: minCheckpointPageCount * 10 Litestream Minimum Checkpoint Page Count The WAL page count at which SQLite attempts a PASSIVE checkpoint, which transfers pages to the main database file without blocking writers. Defaults to checkpointThresholdMB * 250 (since SQLite page size is 4KB). flag: --litestream-min-checkpoint-page-count env: ZERO_LITESTREAM_MIN_CHECKPOINT_PAGE_COUNT default: checkpointThresholdMB * 250 Litestream Multipart Concurrency The number of parts (of size --litestream-multipart-size bytes) to upload or download in parallel when backing up or restoring the snapshot. flag: --litestream-multipart-concurrency env: ZERO_LITESTREAM_MULTIPART_CONCURRENCY default: 48 Litestream Multipart Size The size of each part when uploading or downloading the snapshot with --litestream-multipart-concurrency. Note that up to concurrency * size bytes of memory are used when backing up or restoring the snapshot. flag: --litestream-multipart-size env: ZERO_LITESTREAM_MULTIPART_SIZE default: 16777216 (16 MiB) Litestream Log Level flag: --litestream-log-level env: ZERO_LITESTREAM_LOG_LEVEL default: warn values: debug, info, warn, error Litestream Port Port on which litestream exports metrics, used to determine the replication watermark up to which it is safe to purge change log records. flag: --litestream-port env: ZERO_LITESTREAM_PORT default: --port + 2 Litestream Region The AWS region for the litestream backup bucket. Required for non-standard AWS partitions (e.g. GovCloud us-gov-west-1) where Litestream cannot auto-detect the region. The replication-manager and view-syncers must have the same region. flag: --litestream-region env: ZERO_LITESTREAM_REGION Litestream Restore Parallelism The number of WAL files to download in parallel when performing the initial restore of the replica from the backup. flag: --litestream-restore-parallelism env: ZERO_LITESTREAM_RESTORE_PARALLELISM default: 48 Litestream Snapshot Backup Interval Hours The interval between snapshot backups of the replica. Snapshot backups make a full copy of the database to a new litestream generation. This improves restore time at the expense of bandwidth. Applications with a large database and low write rate can increase this interval to reduce network usage for backups (litestream defaults to 24 hours). flag: --litestream-snapshot-backup-interval-hours env: ZERO_LITESTREAM_SNAPSHOT_BACKUP_INTERVAL_HOURS default: 12 Log Format Use text for developer-friendly console logging and json for consumption by structured-logging services. flag: --log-format env: ZERO_LOG_FORMAT default: \"text\" values: text, json Log IVM Sampling How often to collect IVM metrics. 1 out of N requests will be sampled where N is this value. flag: --log-ivm-sampling env: ZERO_LOG_IVM_SAMPLING default: 5000 Log Level Sets the logging level for the application. flag: --log-level env: ZERO_LOG_LEVEL default: \"info\" values: debug, info, warn, error Log Slow Hydrate Threshold The number of milliseconds a query hydration must take to print a slow warning. flag: --log-slow-hydrate-threshold env: ZERO_LOG_SLOW_HYDRATE_THRESHOLD default: 100 Log Slow Row Threshold The number of ms a row must take to fetch from table-source before it is considered slow. flag: --log-slow-row-threshold env: ZERO_LOG_SLOW_ROW_THRESHOLD default: 2 Mutate API Key An optional secret used to authorize zero-cache to call the API server handling writes. This is sent from zero-cache to your mutate endpoint in an X-Api-Key header. flag: --mutate-api-key env: ZERO_MUTATE_API_KEY Mutate Allowed Client Headers Comma-separated allowlist of client-provided custom headers to forward to your mutate endpoint. Header names are matched case-insensitively. By default, no client-provided custom headers are forwarded. flag: --mutate-allowed-client-headers env: ZERO_MUTATE_ALLOWED_CLIENT_HEADERS default: none Mutate Allowed Request Headers Comma-separated allowlist of HTTP headers from the request that opened the WebSocket to forward to your mutate endpoint. Use this for proxy- or load-balancer-injected headers such as x-forwarded-for or cf-ray. Unlike mutate allowed client headers, these values come from the request that established the connection. Header names are matched case-insensitively. Values are retained for the WebSocket's lifetime, so clients must reconnect to receive changes. The allowlist does not verify the header source - only allow headers that a trusted proxy overwrites or removes from untrusted requests. No request headers are forwarded by default. flag: --mutate-allowed-request-headers env: ZERO_MUTATE_ALLOWED_REQUEST_HEADERS default: none Mutate Forward Cookies If true, zero-cache will forward cookies from the request to zero-cache to your mutate endpoint. This is useful for passing authentication cookies to the API server. If false, cookies are not forwarded. flag: --mutate-forward-cookies env: ZERO_MUTATE_FORWARD_COOKIES default: false Mutate URL The URL of the API server to which zero-cache will push mutations. URLs are matched using URLPattern, a standard Web API. Pattern syntax (similar to Express routes): Exact URL match: \"https://api.example.com/mutate\" Any subdomain using wildcard: \"https://*.example.com/mutate\" Multiple subdomain levels: \"https://*.*.example.com/mutate\" Any path under a domain: \"https://api.example.com/*\" Named path parameters: \"https://api.example.com/:version/mutate\" Matches https://api.example.com/v1/mutate, https://api.example.com/v2/mutate, etc. Advanced patterns: Optional path segments: \"https://api.example.com/:path?\" Regex in segments (for specific patterns): \"https://api.example.com/:version(v\\\\d+)/mutate\" matches only v followed by digits. Multiple patterns can be specified, for example: https://api1.example.com/mutate,https://api2.example.com/mutate Query parameters and URL fragments (#) are ignored during matching. See URLPattern for full syntax. flag: --mutate-url env: ZERO_MUTATE_URL Number of Sync Workers The number of processes to use for view syncing. Leave this unset to use max(1, availableParallelism() - 1), reserving one core for the replicator. If set to 0, the server runs without sync workers, which is the configuration for running the replication-manager in multi-node deployments. flag: --num-sync-workers env: ZERO_NUM_SYNC_WORKERS Per User Mutation Limit Max The maximum mutations per user within the specified windowMs. flag: --per-user-mutation-limit-max env: ZERO_PER_USER_MUTATION_LIMIT_MAX Per User Mutation Limit Window (ms) The sliding window over which the perUserMutationLimitMax is enforced. flag: --per-user-mutation-limit-window-ms env: ZERO_PER_USER_MUTATION_LIMIT_WINDOW_MS default: 60000 PG Replication Slot Failover For upstream Postgres 17+, creates replication slots with the failover flag enabled so they can be synchronized to a standby and survive a failover. This requires additional Postgres-side configuration on your provider; see High Availability and Failover. Has no effect on Postgres versions before 17. flag: --upstream-pg-replication-slot-failover env: ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER default: false Port The port for sync connections. flag: --port env: ZERO_PORT default: 4848 Query API Key An optional secret used to authorize zero-cache to call the API server handling queries. This is sent from zero-cache to your query endpoint in an X-Api-Key header. flag: --query-api-key env: ZERO_QUERY_API_KEY Query Allowed Client Headers Comma-separated allowlist of client-provided custom headers to forward to your query endpoint. Header names are matched case-insensitively. By default, no client-provided custom headers are forwarded. flag: --query-allowed-client-headers env: ZERO_QUERY_ALLOWED_CLIENT_HEADERS default: none Query Allowed Request Headers Comma-separated allowlist of HTTP headers from the request that opened the WebSocket to forward to your query endpoint. Use this for proxy- or load-balancer-injected headers such as x-forwarded-for or cf-ray. Unlike query allowed client headers, these values come from the request that established the connection. Header names are matched case-insensitively. Values are retained for the WebSocket's lifetime, so clients must reconnect to receive changes. The allowlist does not verify the header source - only allow headers that a trusted proxy overwrites or removes from untrusted requests. No request headers are forwarded by default. flag: --query-allowed-request-headers env: ZERO_QUERY_ALLOWED_REQUEST_HEADERS default: none Query Forward Cookies If true, zero-cache will forward cookies from the request to zero-cache to your query endpoint. This is useful for passing authentication cookies to the API server. If false, cookies are not forwarded. flag: --query-forward-cookies env: ZERO_QUERY_FORWARD_COOKIES default: false Query Hydration Stats Track and log the number of rows considered by query hydrations which take longer than log-slow-hydrate-threshold milliseconds. This is useful for debugging and performance tuning. flag: --query-hydration-stats env: ZERO_QUERY_HYDRATION_STATS Query URL The URL of the API server to which zero-cache will send synced queries. URLs are matched using URLPattern, a standard Web API. Pattern syntax (similar to Express routes): Exact URL match: \"https://api.example.com/query\" Any subdomain using wildcard: \"https://*.example.com/query\" Multiple subdomain levels: \"https://*.*.example.com/query\" Any path under a domain: \"https://api.example.com/*\" Named path parameters: \"https://api.example.com/:version/query\" Matches https://api.example.com/v1/query, https://api.example.com/v2/query, etc. Advanced patterns: Optional path segments: \"https://api.example.com/:path?\" Regex in segments (for specific patterns): \"https://api.example.com/:version(v\\\\d+)/query\" matches only v followed by digits. Multiple patterns can be specified, for example: https://api1.example.com/query,https://api2.example.com/query Query parameters and URL fragments (#) are ignored during matching. See URLPattern for full syntax. flag: --query-url env: ZERO_QUERY_URL Replica File File path to the SQLite replica that zero-cache maintains. This can be lost, but if it is, zero-cache will have to re-replicate next time it starts up. flag: --replica-file env: ZERO_REPLICA_FILE default: \"zero.db\" Replica Vacuum Interval Hours Performs a VACUUM at server startup if the specified number of hours has elapsed since the last VACUUM (or initial-sync). The VACUUM operation is heavyweight and requires double the size of the db in disk space. If unspecified, VACUUM operations are not performed. flag: --replica-vacuum-interval-hours env: ZERO_REPLICA_VACUUM_INTERVAL_HOURS Replication Lag Report Interval (ms) The minimum interval at which replication lag reports are written upstream and reported via the zero.replication.total_lag OpenTelemetry metric. Because replication lag reports are only issued after the previous one was received, the actual interval between reports may be longer when there is a backlog in the replication stream. This feature requires write access to upstream Postgres (uses pg_logical_emit_message()). For PostgreSQL 17+, lag measurements accurately reflect committed write latency (single-digit milliseconds). For PostgreSQL 16 and earlier, measurements may appear 50-100ms longer due to flush behavior. A negative or 0 value disables lag reporting. Even if otel is not enabled, info and warn-level logs are emitted for large lag values. flag: --replication-lag-report-interval-ms env: ZERO_REPLICATION_LAG_REPORT_INTERVAL_MS default: 30_000 Server Version The version string outputted to logs when the server starts up. flag: --server-version env: ZERO_SERVER_VERSION Shadow Sync Enabled Periodically exercises the initial-sync code path against a sample of rows from every published table, writing to a throwaway SQLite database. This acts as a canary: if the real initial-sync path breaks because of schema drift, Postgres version quirks, or another full-resync issue, the shadow run fails before a customer actually needs a full reset. flag: --shadow-sync-enabled env: ZERO_SHADOW_SYNC_ENABLED default: false Shadow Sync Interval Hours The interval between shadow initial-sync runs, in hours. The first run fires within [2/3, 1) of this interval after startup, so the canary completes at least once per task lifetime while still jittering fleet restarts. flag: --shadow-sync-interval-hours env: ZERO_SHADOW_SYNC_INTERVAL_HOURS default: 12 Shadow Sync Sample Rate The Bernoulli sampling rate for each table, where 0 < rate <= 1. A value of 1 disables sampling and copies all rows, still subject to --shadow-sync-max-rows-per-table. flag: --shadow-sync-sample-rate env: ZERO_SHADOW_SYNC_SAMPLE_RATE default: 0.1 Shadow Sync Max Rows Per Table The hard upper bound on rows copied per table per shadow run. This guards against unexpectedly large tables consuming too much disk or upstream bandwidth. flag: --shadow-sync-max-rows-per-table env: ZERO_SHADOW_SYNC_MAX_ROWS_PER_TABLE default: 10000 Storage DB Temp Dir Temporary directory for IVM operator storage. Leave unset to use os.tmpdir(). flag: --storage-db-tmp-dir env: ZERO_STORAGE_DB_TMP_DIR Task ID Globally unique identifier for the zero-cache instance. Setting this to a platform specific task identifier can be useful for debugging. If unspecified, zero-cache will attempt to extract the TaskARN if run from within an AWS ECS container, and otherwise use a random string. flag: --task-id env: ZERO_TASK_ID Upstream Max Connections The maximum number of connections to open to the upstream database for committing mutations. This is divided evenly amongst sync workers. In addition to this number, zero-cache uses one connection for the replication stream. Note that this number must allow for at least one connection per sync worker, or zero-cache will fail to start. See num-sync-workers. flag: --upstream-max-conns env: ZERO_UPSTREAM_MAX_CONNS default: 20 Upstream PG Replication Slot Failover For upstream PostgreSQL 17 and later, create replication slots with the failover parameter set to true to enable slot synchronization and failover. Additional Postgres-level configuration is required when enabling this option. This option has no effect for PostgreSQL versions before 17. See the PostgreSQL docs for details: https://www.postgresql.org/docs/current/logicaldecoding-explanation.html#LOGICALDECODING-REPLICATION-SLOTS-SYNCHRONIZATION flag: --upstream-pg-replication-slot-failover env: ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER default: false Websocket Compression Enable WebSocket per-message deflate compression. Compression can reduce bandwidth usage for sync traffic but increases CPU usage on both client and server. Disabled by default. See: https://github.com/websockets/ws#websocket-compression flag: --websocket-compression env: ZERO_WEBSOCKET_COMPRESSION default: false Websocket Compression Options JSON string containing WebSocket compression options. Only used if websocket-compression is enabled. Example: {\"zlibDeflateOptions\":{\"level\":3},\"threshold\":1024}. See https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback for available options. flag: --websocket-compression-options env: ZERO_WEBSOCKET_COMPRESSION_OPTIONS Websocket Max Payload Bytes Maximum size of incoming WebSocket messages in bytes. Messages exceeding this limit are rejected before parsing. flag: --websocket-max-payload-bytes env: ZERO_WEBSOCKET_MAX_PAYLOAD_BYTES default: 10485760 (10 MiB) Yield Threshold (ms) The maximum amount of time in milliseconds that a sync worker will spend in IVM (processing query hydration and advancement) before yielding to the event loop. Lower values increase responsiveness and fairness at the cost of reduced throughput. flag: --yield-threshold-ms env: ZERO_YIELD_THRESHOLD_MS default: 10 Deprecated Flags Auth JWK A public key in JWK format used to verify JWTs. Only one of jwk, jwksUrl and secret may be set. flag: --auth-jwk env: ZERO_AUTH_JWK Auth JWKS URL A URL that returns a JWK set used to verify JWTs. Only one of jwk, jwksUrl and secret may be set. flag: --auth-jwks-url env: ZERO_AUTH_JWKS_URL Auth Secret A symmetric key used to verify JWTs. Only one of jwk, jwksUrl and secret may be set. flag: --auth-secret env: ZERO_AUTH_SECRET", + "content": "zero-cache is configured either via CLI flag or environment variable. There is no separate zero.config file. You can also see all available flags by running zero-cache --help. Required Flags Upstream DB The \"upstream\" authoritative postgres database. In the future we will support other types of upstream besides PG. flag: --upstream-db env: ZERO_UPSTREAM_DB required: true Admin Password A password used to administer zero-cache server, for example to access the /statz endpoint and the inspector. This is required in production (when NODE_ENV=production) because we want all Zero servers to be debuggable using admin tools by default, without needing a restart. But we also don't want to expose sensitive data using them. flag: --admin-password env: ZERO_ADMIN_PASSWORD required: in production (when NODE_ENV=production) Optional Flags App ID Unique identifier for the app. Multiple zero-cache apps can run on a single upstream database, each of which is isolated from the others, with its own permissions, sharding (future feature), and change/cvr databases. The metadata of an app is stored in an upstream schema with the same name, e.g. zero, and the metadata for each app shard, e.g. client and mutation ids, is stored in the {app-id}_{#} schema. (Currently there is only a single \"0\" shard, but this will change with sharding). The CVR and Change data are managed in schemas named {app-id}_{shard-num}/cvr and {app-id}_{shard-num}/cdc, respectively, allowing multiple apps and shards to share the same database instance (e.g. a Postgres \"cluster\") for CVR and Change management. Due to constraints on replication slot names, an App ID may only consist of lower-case letters, numbers, and the underscore character. Note that this option is used by both zero-cache and zero-deploy-permissions. flag: --app-id env: ZERO_APP_ID default: zero App Publications Postgres PUBLICATIONs that define the tables and columns to replicate. Publication names may not begin with an underscore, as zero reserves that prefix for internal use. If unspecified, zero-cache will create and use an internal publication that publishes all tables in the public schema, i.e.: CREATE PUBLICATION _{app-id}_public_0 FOR TABLES IN SCHEMA public; Note that changing the set of publications will result in resyncing the replica, which may involve downtime (replication lag) while the new replica is initializing. To change the set of publications without disrupting an existing app, a new app should be created. To use a custom publication, you can create one with: CREATE PUBLICATION zero_data FOR TABLES IN SCHEMA public; -- or, more selectively: CREATE PUBLICATION zero_data FOR TABLE users, orders; Then set the flag to that publication name, e.g.: ZERO_APP_PUBLICATIONS=zero_data. To specify multiple publications, separate them with commas, e.g.: ZERO_APP_PUBLICATIONS=zero_data1,zero_data2. flag: --app-publications env: ZERO_APP_PUBLICATIONS default: _{app-id}_public_0 Auth Revalidate Interval Seconds How often zero-cache re-checks that each live connection is still authorized to use your /query endpoint. On each interval, zero-cache sends a lightweight validation request using that connection's current auth context, such as forwarded cookies or an opaque auth token. If your query endpoint rejects that auth with a 401/403, the connection is disconnected. Use this to bound how long already-open connections can continue after logout, session expiry, token revocation, or other server-side auth changes that happen without a reconnect. Lower values enforce auth changes faster, but send more validation requests to /query. flag: --auth-revalidate-interval-seconds env: ZERO_AUTH_REVALIDATE_INTERVAL_SECONDS default: unset Auth Retransform Interval Seconds How often zero-cache refreshes a client group's synced or named query transformations using one validated connection from that group. This re-runs auth-sensitive query expansion even when the query set itself has not changed. It is useful when your query endpoint generates different ZQL based on current auth or server-side session state, such as roles, organization membership, feature flags, or other permissions-derived context. Use this to bound how long a client group can keep using stale auth-derived query shapes after backend auth state changes. Lower values pick up those changes faster, but do more /query transform work. If clients already call updateAuth whenever auth changes, this mainly serves as a background safety net for out-of-band auth changes. flag: --auth-retransform-interval-seconds env: ZERO_AUTH_RETRANSFORM_INTERVAL_SECONDS default: unset Auto Reset Automatically wipe and resync the replica when replication is halted. This situation can occur for configurations in which the upstream database provider prohibits event trigger creation, preventing the zero-cache from being able to correctly replicate schema changes. For such configurations, an upstream schema change will instead result in halting replication with an error indicating that the replica needs to be reset. When auto-reset is enabled, zero-cache will respond to such situations by shutting down, and when restarted, resetting the replica and all synced clients. This is a heavy-weight operation and can result in user-visible slowness or downtime if compute resources are scarce. flag: --auto-reset env: ZERO_AUTO_RESET default: true Change DB The Postgres database used to store recent replication log entries, in order to sync multiple view-syncers without requiring multiple replication slots on the upstream database. If unspecified, the upstream-db will be used. flag: --change-db env: ZERO_CHANGE_DB Change Max Connections The maximum number of connections to open to the change database. This is used by the change-streamer for catching up zero-cache replication subscriptions. flag: --change-max-conns env: ZERO_CHANGE_MAX_CONNS default: 5 Change Streamer Back Pressure Limit Heap Proportion The percentage of --max-old-space-size to use as a buffer for absorbing replication stream spikes. When the estimated amount of queued data exceeds this threshold, back pressure is applied to the replication stream, delaying downstream sync as a result. The threshold was determined empirically with load testing. Higher thresholds have resulted in OOMs. Note also that the byte-counting logic in the queue is strictly an underestimate of actual memory usage (but importantly, proportionally correct), so the queue is actually using more than what this proportion suggests. This parameter is exported as an emergency knob to reduce the size of the buffer in the event that the server OOMs from back pressure. Resist the urge to increase this proportion, as it is mainly useful for absorbing periodic spikes and does not meaningfully affect steady-state replication throughput; the latter is determined by other factors such as object serialization and PG throughput. In other words, the back pressure limit does not constrain replication throughput; rather, it protects the system when the upstream throughput exceeds the downstream throughput. flag: --change-streamer-back-pressure-limit-heap-proportion env: ZERO_CHANGE_STREAMER_BACK_PRESSURE_LIMIT_HEAP_PROPORTION default: 0.04 Change Streamer Flow Control Consensus Padding Seconds During periodic flow control checks (every 64kb), this is the amount of time to wait after the majority of subscribers have acked, after which replication continues even if some subscribers have yet to ack. This is not a timeout for the entire send; it starts only after the majority of receivers have acked. This allows a bounded amount of time for backlogged subscribers to catch up on each flush without forcing all subscribers to wait for the entire backlog to be processed. It is also useful for mitigating the effect of unresponsive subscribers due to severed WebSocket connections until liveness checks disconnect them. Set this to a negative number to disable early flow control releases. flag: --change-streamer-flow-control-consensus-padding-seconds env: ZERO_CHANGE_STREAMER_FLOW_CONTROL_CONSENSUS_PADDING_SECONDS default: 1 Change Streamer Mode The mode for running or connecting to the change-streamer: dedicated: runs the change-streamer and shuts down when another change-streamer takes over the replication slot. This is appropriate in a single-node configuration, or for the replication-manager in a multi-node configuration. discover: connects to the change-streamer as internally advertised in the change-db. This is appropriate for the view-syncers in a multi-node setup. This may not work in all networking configurations (e.g., some private networking or port forwarding setups). Using ZERO_CHANGE_STREAMER_URI with an explicit routable hostname is recommended instead. This option is ignored if ZERO_CHANGE_STREAMER_URI is set. flag: --change-streamer-mode env: ZERO_CHANGE_STREAMER_MODE default: dedicated Change Streamer Port The port on which the change-streamer runs. This is an internal protocol between the replication-manager and view-syncers, which runs in the same process tree in local development or a single-node configuration. If unspecified, defaults to --port + 1. flag: --change-streamer-port env: ZERO_CHANGE_STREAMER_PORT default: --port + 1 Change Streamer Startup Delay (ms) The delay to wait before the change-streamer takes over the replication stream (i.e. the handoff during replication-manager updates), to allow load balancers to register the task as healthy based on healthcheck parameters. If a change stream request is received during this interval, the delay will be canceled and the takeover will happen immediately, since the incoming request indicates that the task is registered as a target. flag: --change-streamer-startup-delay-ms env: ZERO_CHANGE_STREAMER_STARTUP_DELAY_MS default: 15000 Change Streamer URI When set, connects to the change-streamer at the given URI. In a multi-node setup, this should be specified in view-syncer options, pointing to the replication-manager URI, which runs a change-streamer on port 4849. flag: --change-streamer-uri env: ZERO_CHANGE_STREAMER_URI CVR DB The Postgres database used to store CVRs. CVRs (client view records) keep track of the data synced to clients in order to determine the diff to send on reconnect. If unspecified, the upstream-db will be used. flag: --cvr-db env: ZERO_CVR_DB CVR Garbage Collection Inactivity Threshold Hours The duration after which an inactive CVR is eligible for garbage collection. Garbage collection is incremental and periodic, so eligible CVRs are not necessarily purged immediately. flag: --cvr-garbage-collection-inactivity-threshold-hours env: ZERO_CVR_GARBAGE_COLLECTION_INACTIVITY_THRESHOLD_HOURS default: 48 CVR Garbage Collection Initial Batch Size The initial number of CVRs to purge per garbage collection interval. This number is increased linearly if the rate of new CVRs exceeds the rate of purged CVRs, in order to reach a steady state. Setting this to 0 effectively disables CVR garbage collection. flag: --cvr-garbage-collection-initial-batch-size env: ZERO_CVR_GARBAGE_COLLECTION_INITIAL_BATCH_SIZE default: 25 CVR Garbage Collection Initial Interval Seconds The initial interval at which to check and garbage collect inactive CVRs. This interval is increased exponentially (up to 16 minutes) when there is nothing to purge. flag: --cvr-garbage-collection-initial-interval-seconds env: ZERO_CVR_GARBAGE_COLLECTION_INITIAL_INTERVAL_SECONDS default: 60 CVR Max Connections The maximum number of connections to open to the CVR database. This is divided evenly amongst sync workers. Note that this number must allow for at least one connection per sync worker, or zero-cache will fail to start. See num-sync-workers. flag: --cvr-max-conns env: ZERO_CVR_MAX_CONNS default: 30 Enable Query Planner Enable the query planner for optimizing ZQL queries. The query planner analyzes and optimizes query execution by determining the most efficient join strategies. You can disable the planner if it is picking bad strategies. flag: --enable-query-planner env: ZERO_ENABLE_QUERY_PLANNER default: true Enable CRUD Mutations Enables support for legacy CRUD mutations. When this is false, view-syncers do not connect to the upstream database for CRUD writes, and push messages with CRUD mutations return an error response. flag: --enable-crud-mutations env: ZERO_ENABLE_CRUD_MUTATIONS default: true Enable Telemetry Zero collects anonymous telemetry data to help us understand usage. We collect: Zero version Uptime General machine information, like the number of CPUs, OS, CI/CD environment, etc. Information about usage, such as number of queries or mutations processed per hour. This is completely optional and can be disabled at any time. You can also opt-out by setting DO_NOT_TRACK=1. flag: --enable-telemetry env: ZERO_ENABLE_TELEMETRY default: true Initial Sync Table Copy Workers The number of parallel workers used to copy tables during initial sync. Each worker uses a database connection, copies a single table at a time, and buffers up to (approximately) 10 MB of table data in memory during initial sync. Increasing the number of workers may improve initial sync speed; however, local disk throughput (IOPS), upstream CPU, and network bandwidth may also be bottlenecks. flag: --initial-sync-table-copy-workers env: ZERO_INITIAL_SYNC_TABLE_COPY_WORKERS default: 5 Lazy Startup Delay starting the majority of zero-cache until first request. This is mainly intended to avoid connecting to Postgres replication stream until the first request is received, which can be useful i.e., for preview instances. Currently only supported in single-node mode. flag: --lazy-startup env: ZERO_LAZY_STARTUP default: false Litestream Backup URL The location of the litestream backup, usually an s3:// URL. This is only consulted by the replication-manager. view-syncers receive this information from the replication-manager. In multi-node deployments, this is required on the replication-manager so view-syncers can reserve snapshots; in single-node deployments it is optional. flag: --litestream-backup-url env: ZERO_LITESTREAM_BACKUP_URL Litestream Endpoint The S3-compatible endpoint URL to use for the litestream backup. This is only required for non-AWS services. The replication-manager and view-syncers must have the same endpoint. For example, to use Cloudflare R2: https://.r2.cloudflarestorage.com. flag: --litestream-endpoint env: ZERO_LITESTREAM_ENDPOINT Litestream Checkpoint Threshold MB The size of the WAL file at which to perform an SQlite checkpoint to apply the writes in the WAL to the main database file. Each checkpoint creates a new WAL segment file that will be backed up by litestream. Smaller thresholds may improve read performance, at the expense of creating more files to download when restoring the replica from the backup. flag: --litestream-checkpoint-threshold-mb env: ZERO_LITESTREAM_CHECKPOINT_THRESHOLD_MB default: 40 Litestream Config Path Path to the litestream yaml config file. zero-cache will run this with its environment variables, which can be referenced in the file via ${ENV} substitution, for example: ZERO_REPLICA_FILE for the db Path ZERO_LITESTREAM_BACKUP_LOCATION for the db replica url ZERO_LITESTREAM_LOG_LEVEL for the log Level ZERO_LOG_FORMAT for the log type flag: --litestream-config-path env: ZERO_LITESTREAM_CONFIG_PATH default: ./src/services/litestream/config.yml Litestream Executable Path to the litestream executable. This must be built from the rocicorp/litestream fork. This option has no effect if litestream-backup-url is unspecified. flag: --litestream-executable env: ZERO_LITESTREAM_EXECUTABLE Litestream Incremental Backup Interval Minutes The interval between incremental backups of the replica. Shorter intervals reduce the amount of change history that needs to be replayed when catching up a new view-syncer, at the expense of increasing the number of files needed to download for the initial litestream restore. flag: --litestream-incremental-backup-interval-minutes env: ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES default: 15 Litestream Maximum Checkpoint Page Count The WAL page count at which SQLite performs a RESTART checkpoint, which blocks writers until complete. Defaults to minCheckpointPageCount * 10. Set to 0 to disable RESTART checkpoints entirely. flag: --litestream-max-checkpoint-page-count env: ZERO_LITESTREAM_MAX_CHECKPOINT_PAGE_COUNT default: minCheckpointPageCount * 10 Litestream Minimum Checkpoint Page Count The WAL page count at which SQLite attempts a PASSIVE checkpoint, which transfers pages to the main database file without blocking writers. Defaults to checkpointThresholdMB * 250 (since SQLite page size is 4KB). flag: --litestream-min-checkpoint-page-count env: ZERO_LITESTREAM_MIN_CHECKPOINT_PAGE_COUNT default: checkpointThresholdMB * 250 Litestream Multipart Concurrency The number of parts (of size --litestream-multipart-size bytes) to upload or download in parallel when backing up or restoring the snapshot. flag: --litestream-multipart-concurrency env: ZERO_LITESTREAM_MULTIPART_CONCURRENCY default: 48 Litestream Multipart Size The size of each part when uploading or downloading the snapshot with --litestream-multipart-concurrency. Note that up to concurrency * size bytes of memory are used when backing up or restoring the snapshot. flag: --litestream-multipart-size env: ZERO_LITESTREAM_MULTIPART_SIZE default: 16777216 (16 MiB) Litestream Log Level flag: --litestream-log-level env: ZERO_LITESTREAM_LOG_LEVEL default: warn values: debug, info, warn, error Litestream Port Port on which litestream exports metrics, used to determine the replication watermark up to which it is safe to purge change log records. flag: --litestream-port env: ZERO_LITESTREAM_PORT default: --port + 2 Litestream Region The AWS region for the litestream backup bucket. Required for non-standard AWS partitions (e.g. GovCloud us-gov-west-1) where Litestream cannot auto-detect the region. The replication-manager and view-syncers must have the same region. flag: --litestream-region env: ZERO_LITESTREAM_REGION Litestream Restore Parallelism The number of WAL files to download in parallel when performing the initial restore of the replica from the backup. flag: --litestream-restore-parallelism env: ZERO_LITESTREAM_RESTORE_PARALLELISM default: 48 Litestream Snapshot Backup Interval Hours The interval between snapshot backups of the replica. Snapshot backups make a full copy of the database to a new litestream generation. This improves restore time at the expense of bandwidth. Applications with a large database and low write rate can increase this interval to reduce network usage for backups (litestream defaults to 24 hours). flag: --litestream-snapshot-backup-interval-hours env: ZERO_LITESTREAM_SNAPSHOT_BACKUP_INTERVAL_HOURS default: 12 Log Format Use text for developer-friendly console logging and json for consumption by structured-logging services. flag: --log-format env: ZERO_LOG_FORMAT default: \"text\" values: text, json Log IVM Sampling How often to collect IVM metrics. 1 out of N requests will be sampled where N is this value. flag: --log-ivm-sampling env: ZERO_LOG_IVM_SAMPLING default: 5000 Log Level Sets the logging level for the application. flag: --log-level env: ZERO_LOG_LEVEL default: \"info\" values: debug, info, warn, error Log Slow Hydrate Threshold The number of milliseconds a query hydration must take to print a slow warning. flag: --log-slow-hydrate-threshold env: ZERO_LOG_SLOW_HYDRATE_THRESHOLD default: 100 Log Slow Row Threshold The number of ms a row must take to fetch from table-source before it is considered slow. flag: --log-slow-row-threshold env: ZERO_LOG_SLOW_ROW_THRESHOLD default: 2 Mutate API Key An optional secret used to authorize zero-cache to call the API server handling writes. This is sent from zero-cache to your mutate endpoint in an X-Api-Key header. flag: --mutate-api-key env: ZERO_MUTATE_API_KEY Mutate Allowed Client Headers Comma-separated allowlist of client-provided custom headers to forward to your mutate endpoint. Header names are matched case-insensitively. By default, no client-provided custom headers are forwarded. flag: --mutate-allowed-client-headers env: ZERO_MUTATE_ALLOWED_CLIENT_HEADERS default: none Mutate Allowed Request Headers Comma-separated allowlist of HTTP headers from the request that opened the WebSocket to forward to your mutate endpoint. Use this for proxy- or load-balancer-injected headers such as x-forwarded-for or cf-ray. Unlike mutate allowed client headers, these values come from the request that established the connection. Header names are matched case-insensitively. Values are retained for the WebSocket's lifetime, so clients must reconnect to receive changes. The allowlist does not verify the header source - only allow headers that a trusted proxy overwrites or removes from untrusted requests. No request headers are forwarded by default. flag: --mutate-allowed-request-headers env: ZERO_MUTATE_ALLOWED_REQUEST_HEADERS default: none Mutate Forward Cookies If true, zero-cache will forward cookies from the request to zero-cache to your mutate endpoint. This is useful for passing authentication cookies to the API server. If false, cookies are not forwarded. flag: --mutate-forward-cookies env: ZERO_MUTATE_FORWARD_COOKIES default: false Mutate URL The URL of the API server to which zero-cache will push mutations. URLs are matched using URLPattern, a standard Web API. Pattern syntax (similar to Express routes): Exact URL match: \"https://api.example.com/mutate\" Any subdomain using wildcard: \"https://*.example.com/mutate\" Multiple subdomain levels: \"https://*.*.example.com/mutate\" Any path under a domain: \"https://api.example.com/*\" Named path parameters: \"https://api.example.com/:version/mutate\" Matches https://api.example.com/v1/mutate, https://api.example.com/v2/mutate, etc. Advanced patterns: Optional path segments: \"https://api.example.com/:path?\" Regex in segments (for specific patterns): \"https://api.example.com/:version(v\\\\d+)/mutate\" matches only v followed by digits. Multiple patterns can be specified, for example: https://api1.example.com/mutate,https://api2.example.com/mutate Query parameters and URL fragments (#) are ignored during matching. See URLPattern for full syntax. flag: --mutate-url env: ZERO_MUTATE_URL Number of Sync Workers The number of processes to use for view syncing. Leave this unset to use max(1, availableParallelism() - 1), reserving one core for the replicator. If set to 0, the server runs without sync workers, which is the configuration for running the replication-manager in multi-node deployments. flag: --num-sync-workers env: ZERO_NUM_SYNC_WORKERS Per User Mutation Limit Max The maximum mutations per user within the specified windowMs. flag: --per-user-mutation-limit-max env: ZERO_PER_USER_MUTATION_LIMIT_MAX Per User Mutation Limit Window (ms) The sliding window over which the perUserMutationLimitMax is enforced. flag: --per-user-mutation-limit-window-ms env: ZERO_PER_USER_MUTATION_LIMIT_WINDOW_MS default: 60000 PG Replication Slot Failover For upstream Postgres 17+, creates replication slots with the failover flag enabled so they can be synchronized to a standby and survive a failover. This requires additional Postgres-side configuration on your provider; see High Availability. Has no effect on Postgres versions before 17. flag: --upstream-pg-replication-slot-failover env: ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER default: false Port The port for sync connections. flag: --port env: ZERO_PORT default: 4848 Query API Key An optional secret used to authorize zero-cache to call the API server handling queries. This is sent from zero-cache to your query endpoint in an X-Api-Key header. flag: --query-api-key env: ZERO_QUERY_API_KEY Query Allowed Client Headers Comma-separated allowlist of client-provided custom headers to forward to your query endpoint. Header names are matched case-insensitively. By default, no client-provided custom headers are forwarded. flag: --query-allowed-client-headers env: ZERO_QUERY_ALLOWED_CLIENT_HEADERS default: none Query Allowed Request Headers Comma-separated allowlist of HTTP headers from the request that opened the WebSocket to forward to your query endpoint. Use this for proxy- or load-balancer-injected headers such as x-forwarded-for or cf-ray. Unlike query allowed client headers, these values come from the request that established the connection. Header names are matched case-insensitively. Values are retained for the WebSocket's lifetime, so clients must reconnect to receive changes. The allowlist does not verify the header source - only allow headers that a trusted proxy overwrites or removes from untrusted requests. No request headers are forwarded by default. flag: --query-allowed-request-headers env: ZERO_QUERY_ALLOWED_REQUEST_HEADERS default: none Query Forward Cookies If true, zero-cache will forward cookies from the request to zero-cache to your query endpoint. This is useful for passing authentication cookies to the API server. If false, cookies are not forwarded. flag: --query-forward-cookies env: ZERO_QUERY_FORWARD_COOKIES default: false Query Hydration Stats Track and log the number of rows considered by query hydrations which take longer than log-slow-hydrate-threshold milliseconds. This is useful for debugging and performance tuning. flag: --query-hydration-stats env: ZERO_QUERY_HYDRATION_STATS Query URL The URL of the API server to which zero-cache will send synced queries. URLs are matched using URLPattern, a standard Web API. Pattern syntax (similar to Express routes): Exact URL match: \"https://api.example.com/query\" Any subdomain using wildcard: \"https://*.example.com/query\" Multiple subdomain levels: \"https://*.*.example.com/query\" Any path under a domain: \"https://api.example.com/*\" Named path parameters: \"https://api.example.com/:version/query\" Matches https://api.example.com/v1/query, https://api.example.com/v2/query, etc. Advanced patterns: Optional path segments: \"https://api.example.com/:path?\" Regex in segments (for specific patterns): \"https://api.example.com/:version(v\\\\d+)/query\" matches only v followed by digits. Multiple patterns can be specified, for example: https://api1.example.com/query,https://api2.example.com/query Query parameters and URL fragments (#) are ignored during matching. See URLPattern for full syntax. flag: --query-url env: ZERO_QUERY_URL Replica File File path to the SQLite replica that zero-cache maintains. This can be lost, but if it is, zero-cache will have to re-replicate next time it starts up. flag: --replica-file env: ZERO_REPLICA_FILE default: \"zero.db\" Replica Vacuum Interval Hours Performs a VACUUM at server startup if the specified number of hours has elapsed since the last VACUUM (or initial-sync). The VACUUM operation is heavyweight and requires double the size of the db in disk space. If unspecified, VACUUM operations are not performed. flag: --replica-vacuum-interval-hours env: ZERO_REPLICA_VACUUM_INTERVAL_HOURS Replication Lag Report Interval (ms) The minimum interval at which replication lag reports are written upstream and reported via the zero.replication.total_lag OpenTelemetry metric. If an expected report is not received before the next interval, Zero emits a new report and increments zero.replication.lag_report_retries. This feature requires write access to upstream Postgres (uses pg_logical_emit_message()). For PostgreSQL 17+, lag measurements accurately reflect committed write latency (single-digit milliseconds). For PostgreSQL 16 and earlier, measurements may appear 50-100ms longer due to flush behavior. A negative or 0 value disables lag reporting. Even if otel is not enabled, info and warn-level logs are emitted for large lag values. flag: --replication-lag-report-interval-ms env: ZERO_REPLICATION_LAG_REPORT_INTERVAL_MS default: 30_000 Server Version The version string outputted to logs when the server starts up. flag: --server-version env: ZERO_SERVER_VERSION Shadow Sync Enabled Periodically exercises the initial-sync code path against a sample of rows from every published table, writing to a throwaway SQLite database. This acts as a canary: if the real initial-sync path breaks because of schema drift, Postgres version quirks, or another full-resync issue, the shadow run fails before a customer actually needs a full reset. flag: --shadow-sync-enabled env: ZERO_SHADOW_SYNC_ENABLED default: false Shadow Sync Interval Hours The interval between shadow initial-sync runs, in hours. The first run fires within [2/3, 1) of this interval after startup, so the canary completes at least once per task lifetime while still jittering fleet restarts. flag: --shadow-sync-interval-hours env: ZERO_SHADOW_SYNC_INTERVAL_HOURS default: 12 Shadow Sync Sample Rate The Bernoulli sampling rate for each table, where 0 < rate <= 1. A value of 1 disables sampling and copies all rows, still subject to --shadow-sync-max-rows-per-table. flag: --shadow-sync-sample-rate env: ZERO_SHADOW_SYNC_SAMPLE_RATE default: 0.1 Shadow Sync Max Rows Per Table The hard upper bound on rows copied per table per shadow run. This guards against unexpectedly large tables consuming too much disk or upstream bandwidth. flag: --shadow-sync-max-rows-per-table env: ZERO_SHADOW_SYNC_MAX_ROWS_PER_TABLE default: 10000 Storage DB Temp Dir Temporary directory for IVM operator storage. Leave unset to use os.tmpdir(). flag: --storage-db-tmp-dir env: ZERO_STORAGE_DB_TMP_DIR Task ID Globally unique identifier for the zero-cache instance. Setting this to a platform specific task identifier can be useful for debugging. If unspecified, zero-cache will attempt to extract the TaskARN if run from within an AWS ECS container, and otherwise use a random string. flag: --task-id env: ZERO_TASK_ID Upstream Max Connections The maximum number of connections to open to the upstream database for committing mutations. This is divided evenly amongst sync workers. In addition to this number, zero-cache uses one connection for the replication stream. Note that this number must allow for at least one connection per sync worker, or zero-cache will fail to start. See num-sync-workers. flag: --upstream-max-conns env: ZERO_UPSTREAM_MAX_CONNS default: 20 Upstream PG Replication Slot Failover For upstream PostgreSQL 17 and later, create replication slots with the failover parameter set to true to enable slot synchronization and failover. Additional Postgres-level configuration is required when enabling this option. This option has no effect for PostgreSQL versions before 17. See the PostgreSQL docs for details: https://www.postgresql.org/docs/current/logicaldecoding-explanation.html#LOGICALDECODING-REPLICATION-SLOTS-SYNCHRONIZATION flag: --upstream-pg-replication-slot-failover env: ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER default: false Websocket Compression Enable WebSocket per-message deflate compression. Compression can reduce bandwidth usage for sync traffic but increases CPU usage on both client and server. Disabled by default. See: https://github.com/websockets/ws#websocket-compression flag: --websocket-compression env: ZERO_WEBSOCKET_COMPRESSION default: false Websocket Compression Options JSON string containing WebSocket compression options. Only used if websocket-compression is enabled. Example: {\"zlibDeflateOptions\":{\"level\":3},\"threshold\":1024}. See https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback for available options. flag: --websocket-compression-options env: ZERO_WEBSOCKET_COMPRESSION_OPTIONS Websocket Max Payload Bytes Maximum size of incoming WebSocket messages in bytes. Messages exceeding this limit are rejected before parsing. flag: --websocket-max-payload-bytes env: ZERO_WEBSOCKET_MAX_PAYLOAD_BYTES default: 10485760 (10 MiB) Yield Threshold (ms) The maximum amount of time in milliseconds that a sync worker will spend in IVM (processing query hydration and advancement) before yielding to the event loop. Lower values increase responsiveness and fairness at the cost of reduced throughput. flag: --yield-threshold-ms env: ZERO_YIELD_THRESHOLD_MS default: 10 Deprecated Flags Auth JWK A public key in JWK format used to verify JWTs. Only one of jwk, jwksUrl and secret may be set. flag: --auth-jwk env: ZERO_AUTH_JWK Auth JWKS URL A URL that returns a JWK set used to verify JWTs. Only one of jwk, jwksUrl and secret may be set. flag: --auth-jwks-url env: ZERO_AUTH_JWKS_URL Auth Secret A symmetric key used to verify JWTs. Only one of jwk, jwksUrl and secret may be set. flag: --auth-secret env: ZERO_AUTH_SECRET", "headings": [ { "text": "Required Flags", @@ -8034,7 +8142,7 @@ "kind": "page" }, { - "id": "573-zero-cache-config#required-flags", + "id": "581-zero-cache-config#required-flags", "title": "zero-cache Config", "searchTitle": "Required Flags", "sectionTitle": "Required Flags", @@ -8044,7 +8152,7 @@ "kind": "section" }, { - "id": "574-zero-cache-config#upstream-db", + "id": "582-zero-cache-config#upstream-db", "title": "zero-cache Config", "searchTitle": "Upstream DB", "sectionTitle": "Upstream DB", @@ -8054,7 +8162,7 @@ "kind": "section" }, { - "id": "575-zero-cache-config#admin-password", + "id": "583-zero-cache-config#admin-password", "title": "zero-cache Config", "searchTitle": "Admin Password", "sectionTitle": "Admin Password", @@ -8064,17 +8172,17 @@ "kind": "section" }, { - "id": "576-zero-cache-config#optional-flags", + "id": "584-zero-cache-config#optional-flags", "title": "zero-cache Config", "searchTitle": "Optional Flags", "sectionTitle": "Optional Flags", "sectionId": "optional-flags", "url": "/docs/zero-cache-config", - "content": "App ID Unique identifier for the app. Multiple zero-cache apps can run on a single upstream database, each of which is isolated from the others, with its own permissions, sharding (future feature), and change/cvr databases. The metadata of an app is stored in an upstream schema with the same name, e.g. zero, and the metadata for each app shard, e.g. client and mutation ids, is stored in the {app-id}_{#} schema. (Currently there is only a single \"0\" shard, but this will change with sharding). The CVR and Change data are managed in schemas named {app-id}_{shard-num}/cvr and {app-id}_{shard-num}/cdc, respectively, allowing multiple apps and shards to share the same database instance (e.g. a Postgres \"cluster\") for CVR and Change management. Due to constraints on replication slot names, an App ID may only consist of lower-case letters, numbers, and the underscore character. Note that this option is used by both zero-cache and zero-deploy-permissions. flag: --app-id env: ZERO_APP_ID default: zero App Publications Postgres PUBLICATIONs that define the tables and columns to replicate. Publication names may not begin with an underscore, as zero reserves that prefix for internal use. If unspecified, zero-cache will create and use an internal publication that publishes all tables in the public schema, i.e.: CREATE PUBLICATION _{app-id}_public_0 FOR TABLES IN SCHEMA public; Note that changing the set of publications will result in resyncing the replica, which may involve downtime (replication lag) while the new replica is initializing. To change the set of publications without disrupting an existing app, a new app should be created. To use a custom publication, you can create one with: CREATE PUBLICATION zero_data FOR TABLES IN SCHEMA public; -- or, more selectively: CREATE PUBLICATION zero_data FOR TABLE users, orders; Then set the flag to that publication name, e.g.: ZERO_APP_PUBLICATIONS=zero_data. To specify multiple publications, separate them with commas, e.g.: ZERO_APP_PUBLICATIONS=zero_data1,zero_data2. flag: --app-publications env: ZERO_APP_PUBLICATIONS default: _{app-id}_public_0 Auth Revalidate Interval Seconds How often zero-cache re-checks that each live connection is still authorized to use your /query endpoint. On each interval, zero-cache sends a lightweight validation request using that connection's current auth context, such as forwarded cookies or an opaque auth token. If your query endpoint rejects that auth with a 401/403, the connection is disconnected. Use this to bound how long already-open connections can continue after logout, session expiry, token revocation, or other server-side auth changes that happen without a reconnect. Lower values enforce auth changes faster, but send more validation requests to /query. flag: --auth-revalidate-interval-seconds env: ZERO_AUTH_REVALIDATE_INTERVAL_SECONDS default: unset Auth Retransform Interval Seconds How often zero-cache refreshes a client group's synced or named query transformations using one validated connection from that group. This re-runs auth-sensitive query expansion even when the query set itself has not changed. It is useful when your query endpoint generates different ZQL based on current auth or server-side session state, such as roles, organization membership, feature flags, or other permissions-derived context. Use this to bound how long a client group can keep using stale auth-derived query shapes after backend auth state changes. Lower values pick up those changes faster, but do more /query transform work. If clients already call updateAuth whenever auth changes, this mainly serves as a background safety net for out-of-band auth changes. flag: --auth-retransform-interval-seconds env: ZERO_AUTH_RETRANSFORM_INTERVAL_SECONDS default: unset Auto Reset Automatically wipe and resync the replica when replication is halted. This situation can occur for configurations in which the upstream database provider prohibits event trigger creation, preventing the zero-cache from being able to correctly replicate schema changes. For such configurations, an upstream schema change will instead result in halting replication with an error indicating that the replica needs to be reset. When auto-reset is enabled, zero-cache will respond to such situations by shutting down, and when restarted, resetting the replica and all synced clients. This is a heavy-weight operation and can result in user-visible slowness or downtime if compute resources are scarce. flag: --auto-reset env: ZERO_AUTO_RESET default: true Change DB The Postgres database used to store recent replication log entries, in order to sync multiple view-syncers without requiring multiple replication slots on the upstream database. If unspecified, the upstream-db will be used. flag: --change-db env: ZERO_CHANGE_DB Change Max Connections The maximum number of connections to open to the change database. This is used by the change-streamer for catching up zero-cache replication subscriptions. flag: --change-max-conns env: ZERO_CHANGE_MAX_CONNS default: 5 Change Streamer Back Pressure Limit Heap Proportion The percentage of --max-old-space-size to use as a buffer for absorbing replication stream spikes. When the estimated amount of queued data exceeds this threshold, back pressure is applied to the replication stream, delaying downstream sync as a result. The threshold was determined empirically with load testing. Higher thresholds have resulted in OOMs. Note also that the byte-counting logic in the queue is strictly an underestimate of actual memory usage (but importantly, proportionally correct), so the queue is actually using more than what this proportion suggests. This parameter is exported as an emergency knob to reduce the size of the buffer in the event that the server OOMs from back pressure. Resist the urge to increase this proportion, as it is mainly useful for absorbing periodic spikes and does not meaningfully affect steady-state replication throughput; the latter is determined by other factors such as object serialization and PG throughput. In other words, the back pressure limit does not constrain replication throughput; rather, it protects the system when the upstream throughput exceeds the downstream throughput. flag: --change-streamer-back-pressure-limit-heap-proportion env: ZERO_CHANGE_STREAMER_BACK_PRESSURE_LIMIT_HEAP_PROPORTION default: 0.04 Change Streamer Flow Control Consensus Padding Seconds During periodic flow control checks (every 64kb), this is the amount of time to wait after the majority of subscribers have acked, after which replication continues even if some subscribers have yet to ack. This is not a timeout for the entire send; it starts only after the majority of receivers have acked. This allows a bounded amount of time for backlogged subscribers to catch up on each flush without forcing all subscribers to wait for the entire backlog to be processed. It is also useful for mitigating the effect of unresponsive subscribers due to severed WebSocket connections until liveness checks disconnect them. Set this to a negative number to disable early flow control releases. flag: --change-streamer-flow-control-consensus-padding-seconds env: ZERO_CHANGE_STREAMER_FLOW_CONTROL_CONSENSUS_PADDING_SECONDS default: 1 Change Streamer Mode The mode for running or connecting to the change-streamer: dedicated: runs the change-streamer and shuts down when another change-streamer takes over the replication slot. This is appropriate in a single-node configuration, or for the replication-manager in a multi-node configuration. discover: connects to the change-streamer as internally advertised in the change-db. This is appropriate for the view-syncers in a multi-node setup. This may not work in all networking configurations (e.g., some private networking or port forwarding setups). Using ZERO_CHANGE_STREAMER_URI with an explicit routable hostname is recommended instead. This option is ignored if ZERO_CHANGE_STREAMER_URI is set. flag: --change-streamer-mode env: ZERO_CHANGE_STREAMER_MODE default: dedicated Change Streamer Port The port on which the change-streamer runs. This is an internal protocol between the replication-manager and view-syncers, which runs in the same process tree in local development or a single-node configuration. If unspecified, defaults to --port + 1. flag: --change-streamer-port env: ZERO_CHANGE_STREAMER_PORT default: --port + 1 Change Streamer Startup Delay (ms) The delay to wait before the change-streamer takes over the replication stream (i.e. the handoff during replication-manager updates), to allow load balancers to register the task as healthy based on healthcheck parameters. If a change stream request is received during this interval, the delay will be canceled and the takeover will happen immediately, since the incoming request indicates that the task is registered as a target. flag: --change-streamer-startup-delay-ms env: ZERO_CHANGE_STREAMER_STARTUP_DELAY_MS default: 15000 Change Streamer URI When set, connects to the change-streamer at the given URI. In a multi-node setup, this should be specified in view-syncer options, pointing to the replication-manager URI, which runs a change-streamer on port 4849. flag: --change-streamer-uri env: ZERO_CHANGE_STREAMER_URI CVR DB The Postgres database used to store CVRs. CVRs (client view records) keep track of the data synced to clients in order to determine the diff to send on reconnect. If unspecified, the upstream-db will be used. flag: --cvr-db env: ZERO_CVR_DB CVR Garbage Collection Inactivity Threshold Hours The duration after which an inactive CVR is eligible for garbage collection. Garbage collection is incremental and periodic, so eligible CVRs are not necessarily purged immediately. flag: --cvr-garbage-collection-inactivity-threshold-hours env: ZERO_CVR_GARBAGE_COLLECTION_INACTIVITY_THRESHOLD_HOURS default: 48 CVR Garbage Collection Initial Batch Size The initial number of CVRs to purge per garbage collection interval. This number is increased linearly if the rate of new CVRs exceeds the rate of purged CVRs, in order to reach a steady state. Setting this to 0 effectively disables CVR garbage collection. flag: --cvr-garbage-collection-initial-batch-size env: ZERO_CVR_GARBAGE_COLLECTION_INITIAL_BATCH_SIZE default: 25 CVR Garbage Collection Initial Interval Seconds The initial interval at which to check and garbage collect inactive CVRs. This interval is increased exponentially (up to 16 minutes) when there is nothing to purge. flag: --cvr-garbage-collection-initial-interval-seconds env: ZERO_CVR_GARBAGE_COLLECTION_INITIAL_INTERVAL_SECONDS default: 60 CVR Max Connections The maximum number of connections to open to the CVR database. This is divided evenly amongst sync workers. Note that this number must allow for at least one connection per sync worker, or zero-cache will fail to start. See num-sync-workers. flag: --cvr-max-conns env: ZERO_CVR_MAX_CONNS default: 30 Enable Query Planner Enable the query planner for optimizing ZQL queries. The query planner analyzes and optimizes query execution by determining the most efficient join strategies. You can disable the planner if it is picking bad strategies. flag: --enable-query-planner env: ZERO_ENABLE_QUERY_PLANNER default: true Enable CRUD Mutations Enables support for legacy CRUD mutations. When this is false, view-syncers do not connect to the upstream database for CRUD writes, and push messages with CRUD mutations return an error response. flag: --enable-crud-mutations env: ZERO_ENABLE_CRUD_MUTATIONS default: true Enable Telemetry Zero collects anonymous telemetry data to help us understand usage. We collect: Zero version Uptime General machine information, like the number of CPUs, OS, CI/CD environment, etc. Information about usage, such as number of queries or mutations processed per hour. This is completely optional and can be disabled at any time. You can also opt-out by setting DO_NOT_TRACK=1. flag: --enable-telemetry env: ZERO_ENABLE_TELEMETRY default: true Initial Sync Table Copy Workers The number of parallel workers used to copy tables during initial sync. Each worker uses a database connection, copies a single table at a time, and buffers up to (approximately) 10 MB of table data in memory during initial sync. Increasing the number of workers may improve initial sync speed; however, local disk throughput (IOPS), upstream CPU, and network bandwidth may also be bottlenecks. flag: --initial-sync-table-copy-workers env: ZERO_INITIAL_SYNC_TABLE_COPY_WORKERS default: 5 Lazy Startup Delay starting the majority of zero-cache until first request. This is mainly intended to avoid connecting to Postgres replication stream until the first request is received, which can be useful i.e., for preview instances. Currently only supported in single-node mode. flag: --lazy-startup env: ZERO_LAZY_STARTUP default: false Litestream Backup URL The location of the litestream backup, usually an s3:// URL. This is only consulted by the replication-manager. view-syncers receive this information from the replication-manager. In multi-node deployments, this is required on the replication-manager so view-syncers can reserve snapshots; in single-node deployments it is optional. flag: --litestream-backup-url env: ZERO_LITESTREAM_BACKUP_URL Litestream Endpoint The S3-compatible endpoint URL to use for the litestream backup. This is only required for non-AWS services. The replication-manager and view-syncers must have the same endpoint. For example, to use Cloudflare R2: https://.r2.cloudflarestorage.com. flag: --litestream-endpoint env: ZERO_LITESTREAM_ENDPOINT Litestream Checkpoint Threshold MB The size of the WAL file at which to perform an SQlite checkpoint to apply the writes in the WAL to the main database file. Each checkpoint creates a new WAL segment file that will be backed up by litestream. Smaller thresholds may improve read performance, at the expense of creating more files to download when restoring the replica from the backup. flag: --litestream-checkpoint-threshold-mb env: ZERO_LITESTREAM_CHECKPOINT_THRESHOLD_MB default: 40 Litestream Config Path Path to the litestream yaml config file. zero-cache will run this with its environment variables, which can be referenced in the file via ${ENV} substitution, for example: ZERO_REPLICA_FILE for the db Path ZERO_LITESTREAM_BACKUP_LOCATION for the db replica url ZERO_LITESTREAM_LOG_LEVEL for the log Level ZERO_LOG_FORMAT for the log type flag: --litestream-config-path env: ZERO_LITESTREAM_CONFIG_PATH default: ./src/services/litestream/config.yml Litestream Executable Path to the litestream executable. This must be built from the rocicorp/litestream fork. This option has no effect if litestream-backup-url is unspecified. flag: --litestream-executable env: ZERO_LITESTREAM_EXECUTABLE Litestream Incremental Backup Interval Minutes The interval between incremental backups of the replica. Shorter intervals reduce the amount of change history that needs to be replayed when catching up a new view-syncer, at the expense of increasing the number of files needed to download for the initial litestream restore. flag: --litestream-incremental-backup-interval-minutes env: ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES default: 15 Litestream Maximum Checkpoint Page Count The WAL page count at which SQLite performs a RESTART checkpoint, which blocks writers until complete. Defaults to minCheckpointPageCount * 10. Set to 0 to disable RESTART checkpoints entirely. flag: --litestream-max-checkpoint-page-count env: ZERO_LITESTREAM_MAX_CHECKPOINT_PAGE_COUNT default: minCheckpointPageCount * 10 Litestream Minimum Checkpoint Page Count The WAL page count at which SQLite attempts a PASSIVE checkpoint, which transfers pages to the main database file without blocking writers. Defaults to checkpointThresholdMB * 250 (since SQLite page size is 4KB). flag: --litestream-min-checkpoint-page-count env: ZERO_LITESTREAM_MIN_CHECKPOINT_PAGE_COUNT default: checkpointThresholdMB * 250 Litestream Multipart Concurrency The number of parts (of size --litestream-multipart-size bytes) to upload or download in parallel when backing up or restoring the snapshot. flag: --litestream-multipart-concurrency env: ZERO_LITESTREAM_MULTIPART_CONCURRENCY default: 48 Litestream Multipart Size The size of each part when uploading or downloading the snapshot with --litestream-multipart-concurrency. Note that up to concurrency * size bytes of memory are used when backing up or restoring the snapshot. flag: --litestream-multipart-size env: ZERO_LITESTREAM_MULTIPART_SIZE default: 16777216 (16 MiB) Litestream Log Level flag: --litestream-log-level env: ZERO_LITESTREAM_LOG_LEVEL default: warn values: debug, info, warn, error Litestream Port Port on which litestream exports metrics, used to determine the replication watermark up to which it is safe to purge change log records. flag: --litestream-port env: ZERO_LITESTREAM_PORT default: --port + 2 Litestream Region The AWS region for the litestream backup bucket. Required for non-standard AWS partitions (e.g. GovCloud us-gov-west-1) where Litestream cannot auto-detect the region. The replication-manager and view-syncers must have the same region. flag: --litestream-region env: ZERO_LITESTREAM_REGION Litestream Restore Parallelism The number of WAL files to download in parallel when performing the initial restore of the replica from the backup. flag: --litestream-restore-parallelism env: ZERO_LITESTREAM_RESTORE_PARALLELISM default: 48 Litestream Snapshot Backup Interval Hours The interval between snapshot backups of the replica. Snapshot backups make a full copy of the database to a new litestream generation. This improves restore time at the expense of bandwidth. Applications with a large database and low write rate can increase this interval to reduce network usage for backups (litestream defaults to 24 hours). flag: --litestream-snapshot-backup-interval-hours env: ZERO_LITESTREAM_SNAPSHOT_BACKUP_INTERVAL_HOURS default: 12 Log Format Use text for developer-friendly console logging and json for consumption by structured-logging services. flag: --log-format env: ZERO_LOG_FORMAT default: \"text\" values: text, json Log IVM Sampling How often to collect IVM metrics. 1 out of N requests will be sampled where N is this value. flag: --log-ivm-sampling env: ZERO_LOG_IVM_SAMPLING default: 5000 Log Level Sets the logging level for the application. flag: --log-level env: ZERO_LOG_LEVEL default: \"info\" values: debug, info, warn, error Log Slow Hydrate Threshold The number of milliseconds a query hydration must take to print a slow warning. flag: --log-slow-hydrate-threshold env: ZERO_LOG_SLOW_HYDRATE_THRESHOLD default: 100 Log Slow Row Threshold The number of ms a row must take to fetch from table-source before it is considered slow. flag: --log-slow-row-threshold env: ZERO_LOG_SLOW_ROW_THRESHOLD default: 2 Mutate API Key An optional secret used to authorize zero-cache to call the API server handling writes. This is sent from zero-cache to your mutate endpoint in an X-Api-Key header. flag: --mutate-api-key env: ZERO_MUTATE_API_KEY Mutate Allowed Client Headers Comma-separated allowlist of client-provided custom headers to forward to your mutate endpoint. Header names are matched case-insensitively. By default, no client-provided custom headers are forwarded. flag: --mutate-allowed-client-headers env: ZERO_MUTATE_ALLOWED_CLIENT_HEADERS default: none Mutate Allowed Request Headers Comma-separated allowlist of HTTP headers from the request that opened the WebSocket to forward to your mutate endpoint. Use this for proxy- or load-balancer-injected headers such as x-forwarded-for or cf-ray. Unlike mutate allowed client headers, these values come from the request that established the connection. Header names are matched case-insensitively. Values are retained for the WebSocket's lifetime, so clients must reconnect to receive changes. The allowlist does not verify the header source - only allow headers that a trusted proxy overwrites or removes from untrusted requests. No request headers are forwarded by default. flag: --mutate-allowed-request-headers env: ZERO_MUTATE_ALLOWED_REQUEST_HEADERS default: none Mutate Forward Cookies If true, zero-cache will forward cookies from the request to zero-cache to your mutate endpoint. This is useful for passing authentication cookies to the API server. If false, cookies are not forwarded. flag: --mutate-forward-cookies env: ZERO_MUTATE_FORWARD_COOKIES default: false Mutate URL The URL of the API server to which zero-cache will push mutations. URLs are matched using URLPattern, a standard Web API. Pattern syntax (similar to Express routes): Exact URL match: \"https://api.example.com/mutate\" Any subdomain using wildcard: \"https://*.example.com/mutate\" Multiple subdomain levels: \"https://*.*.example.com/mutate\" Any path under a domain: \"https://api.example.com/*\" Named path parameters: \"https://api.example.com/:version/mutate\" Matches https://api.example.com/v1/mutate, https://api.example.com/v2/mutate, etc. Advanced patterns: Optional path segments: \"https://api.example.com/:path?\" Regex in segments (for specific patterns): \"https://api.example.com/:version(v\\\\d+)/mutate\" matches only v followed by digits. Multiple patterns can be specified, for example: https://api1.example.com/mutate,https://api2.example.com/mutate Query parameters and URL fragments (#) are ignored during matching. See URLPattern for full syntax. flag: --mutate-url env: ZERO_MUTATE_URL Number of Sync Workers The number of processes to use for view syncing. Leave this unset to use max(1, availableParallelism() - 1), reserving one core for the replicator. If set to 0, the server runs without sync workers, which is the configuration for running the replication-manager in multi-node deployments. flag: --num-sync-workers env: ZERO_NUM_SYNC_WORKERS Per User Mutation Limit Max The maximum mutations per user within the specified windowMs. flag: --per-user-mutation-limit-max env: ZERO_PER_USER_MUTATION_LIMIT_MAX Per User Mutation Limit Window (ms) The sliding window over which the perUserMutationLimitMax is enforced. flag: --per-user-mutation-limit-window-ms env: ZERO_PER_USER_MUTATION_LIMIT_WINDOW_MS default: 60000 PG Replication Slot Failover For upstream Postgres 17+, creates replication slots with the failover flag enabled so they can be synchronized to a standby and survive a failover. This requires additional Postgres-side configuration on your provider; see High Availability and Failover. Has no effect on Postgres versions before 17. flag: --upstream-pg-replication-slot-failover env: ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER default: false Port The port for sync connections. flag: --port env: ZERO_PORT default: 4848 Query API Key An optional secret used to authorize zero-cache to call the API server handling queries. This is sent from zero-cache to your query endpoint in an X-Api-Key header. flag: --query-api-key env: ZERO_QUERY_API_KEY Query Allowed Client Headers Comma-separated allowlist of client-provided custom headers to forward to your query endpoint. Header names are matched case-insensitively. By default, no client-provided custom headers are forwarded. flag: --query-allowed-client-headers env: ZERO_QUERY_ALLOWED_CLIENT_HEADERS default: none Query Allowed Request Headers Comma-separated allowlist of HTTP headers from the request that opened the WebSocket to forward to your query endpoint. Use this for proxy- or load-balancer-injected headers such as x-forwarded-for or cf-ray. Unlike query allowed client headers, these values come from the request that established the connection. Header names are matched case-insensitively. Values are retained for the WebSocket's lifetime, so clients must reconnect to receive changes. The allowlist does not verify the header source - only allow headers that a trusted proxy overwrites or removes from untrusted requests. No request headers are forwarded by default. flag: --query-allowed-request-headers env: ZERO_QUERY_ALLOWED_REQUEST_HEADERS default: none Query Forward Cookies If true, zero-cache will forward cookies from the request to zero-cache to your query endpoint. This is useful for passing authentication cookies to the API server. If false, cookies are not forwarded. flag: --query-forward-cookies env: ZERO_QUERY_FORWARD_COOKIES default: false Query Hydration Stats Track and log the number of rows considered by query hydrations which take longer than log-slow-hydrate-threshold milliseconds. This is useful for debugging and performance tuning. flag: --query-hydration-stats env: ZERO_QUERY_HYDRATION_STATS Query URL The URL of the API server to which zero-cache will send synced queries. URLs are matched using URLPattern, a standard Web API. Pattern syntax (similar to Express routes): Exact URL match: \"https://api.example.com/query\" Any subdomain using wildcard: \"https://*.example.com/query\" Multiple subdomain levels: \"https://*.*.example.com/query\" Any path under a domain: \"https://api.example.com/*\" Named path parameters: \"https://api.example.com/:version/query\" Matches https://api.example.com/v1/query, https://api.example.com/v2/query, etc. Advanced patterns: Optional path segments: \"https://api.example.com/:path?\" Regex in segments (for specific patterns): \"https://api.example.com/:version(v\\\\d+)/query\" matches only v followed by digits. Multiple patterns can be specified, for example: https://api1.example.com/query,https://api2.example.com/query Query parameters and URL fragments (#) are ignored during matching. See URLPattern for full syntax. flag: --query-url env: ZERO_QUERY_URL Replica File File path to the SQLite replica that zero-cache maintains. This can be lost, but if it is, zero-cache will have to re-replicate next time it starts up. flag: --replica-file env: ZERO_REPLICA_FILE default: \"zero.db\" Replica Vacuum Interval Hours Performs a VACUUM at server startup if the specified number of hours has elapsed since the last VACUUM (or initial-sync). The VACUUM operation is heavyweight and requires double the size of the db in disk space. If unspecified, VACUUM operations are not performed. flag: --replica-vacuum-interval-hours env: ZERO_REPLICA_VACUUM_INTERVAL_HOURS Replication Lag Report Interval (ms) The minimum interval at which replication lag reports are written upstream and reported via the zero.replication.total_lag OpenTelemetry metric. Because replication lag reports are only issued after the previous one was received, the actual interval between reports may be longer when there is a backlog in the replication stream. This feature requires write access to upstream Postgres (uses pg_logical_emit_message()). For PostgreSQL 17+, lag measurements accurately reflect committed write latency (single-digit milliseconds). For PostgreSQL 16 and earlier, measurements may appear 50-100ms longer due to flush behavior. A negative or 0 value disables lag reporting. Even if otel is not enabled, info and warn-level logs are emitted for large lag values. flag: --replication-lag-report-interval-ms env: ZERO_REPLICATION_LAG_REPORT_INTERVAL_MS default: 30_000 Server Version The version string outputted to logs when the server starts up. flag: --server-version env: ZERO_SERVER_VERSION Shadow Sync Enabled Periodically exercises the initial-sync code path against a sample of rows from every published table, writing to a throwaway SQLite database. This acts as a canary: if the real initial-sync path breaks because of schema drift, Postgres version quirks, or another full-resync issue, the shadow run fails before a customer actually needs a full reset. flag: --shadow-sync-enabled env: ZERO_SHADOW_SYNC_ENABLED default: false Shadow Sync Interval Hours The interval between shadow initial-sync runs, in hours. The first run fires within [2/3, 1) of this interval after startup, so the canary completes at least once per task lifetime while still jittering fleet restarts. flag: --shadow-sync-interval-hours env: ZERO_SHADOW_SYNC_INTERVAL_HOURS default: 12 Shadow Sync Sample Rate The Bernoulli sampling rate for each table, where 0 < rate <= 1. A value of 1 disables sampling and copies all rows, still subject to --shadow-sync-max-rows-per-table. flag: --shadow-sync-sample-rate env: ZERO_SHADOW_SYNC_SAMPLE_RATE default: 0.1 Shadow Sync Max Rows Per Table The hard upper bound on rows copied per table per shadow run. This guards against unexpectedly large tables consuming too much disk or upstream bandwidth. flag: --shadow-sync-max-rows-per-table env: ZERO_SHADOW_SYNC_MAX_ROWS_PER_TABLE default: 10000 Storage DB Temp Dir Temporary directory for IVM operator storage. Leave unset to use os.tmpdir(). flag: --storage-db-tmp-dir env: ZERO_STORAGE_DB_TMP_DIR Task ID Globally unique identifier for the zero-cache instance. Setting this to a platform specific task identifier can be useful for debugging. If unspecified, zero-cache will attempt to extract the TaskARN if run from within an AWS ECS container, and otherwise use a random string. flag: --task-id env: ZERO_TASK_ID Upstream Max Connections The maximum number of connections to open to the upstream database for committing mutations. This is divided evenly amongst sync workers. In addition to this number, zero-cache uses one connection for the replication stream. Note that this number must allow for at least one connection per sync worker, or zero-cache will fail to start. See num-sync-workers. flag: --upstream-max-conns env: ZERO_UPSTREAM_MAX_CONNS default: 20 Upstream PG Replication Slot Failover For upstream PostgreSQL 17 and later, create replication slots with the failover parameter set to true to enable slot synchronization and failover. Additional Postgres-level configuration is required when enabling this option. This option has no effect for PostgreSQL versions before 17. See the PostgreSQL docs for details: https://www.postgresql.org/docs/current/logicaldecoding-explanation.html#LOGICALDECODING-REPLICATION-SLOTS-SYNCHRONIZATION flag: --upstream-pg-replication-slot-failover env: ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER default: false Websocket Compression Enable WebSocket per-message deflate compression. Compression can reduce bandwidth usage for sync traffic but increases CPU usage on both client and server. Disabled by default. See: https://github.com/websockets/ws#websocket-compression flag: --websocket-compression env: ZERO_WEBSOCKET_COMPRESSION default: false Websocket Compression Options JSON string containing WebSocket compression options. Only used if websocket-compression is enabled. Example: {\"zlibDeflateOptions\":{\"level\":3},\"threshold\":1024}. See https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback for available options. flag: --websocket-compression-options env: ZERO_WEBSOCKET_COMPRESSION_OPTIONS Websocket Max Payload Bytes Maximum size of incoming WebSocket messages in bytes. Messages exceeding this limit are rejected before parsing. flag: --websocket-max-payload-bytes env: ZERO_WEBSOCKET_MAX_PAYLOAD_BYTES default: 10485760 (10 MiB) Yield Threshold (ms) The maximum amount of time in milliseconds that a sync worker will spend in IVM (processing query hydration and advancement) before yielding to the event loop. Lower values increase responsiveness and fairness at the cost of reduced throughput. flag: --yield-threshold-ms env: ZERO_YIELD_THRESHOLD_MS default: 10", + "content": "App ID Unique identifier for the app. Multiple zero-cache apps can run on a single upstream database, each of which is isolated from the others, with its own permissions, sharding (future feature), and change/cvr databases. The metadata of an app is stored in an upstream schema with the same name, e.g. zero, and the metadata for each app shard, e.g. client and mutation ids, is stored in the {app-id}_{#} schema. (Currently there is only a single \"0\" shard, but this will change with sharding). The CVR and Change data are managed in schemas named {app-id}_{shard-num}/cvr and {app-id}_{shard-num}/cdc, respectively, allowing multiple apps and shards to share the same database instance (e.g. a Postgres \"cluster\") for CVR and Change management. Due to constraints on replication slot names, an App ID may only consist of lower-case letters, numbers, and the underscore character. Note that this option is used by both zero-cache and zero-deploy-permissions. flag: --app-id env: ZERO_APP_ID default: zero App Publications Postgres PUBLICATIONs that define the tables and columns to replicate. Publication names may not begin with an underscore, as zero reserves that prefix for internal use. If unspecified, zero-cache will create and use an internal publication that publishes all tables in the public schema, i.e.: CREATE PUBLICATION _{app-id}_public_0 FOR TABLES IN SCHEMA public; Note that changing the set of publications will result in resyncing the replica, which may involve downtime (replication lag) while the new replica is initializing. To change the set of publications without disrupting an existing app, a new app should be created. To use a custom publication, you can create one with: CREATE PUBLICATION zero_data FOR TABLES IN SCHEMA public; -- or, more selectively: CREATE PUBLICATION zero_data FOR TABLE users, orders; Then set the flag to that publication name, e.g.: ZERO_APP_PUBLICATIONS=zero_data. To specify multiple publications, separate them with commas, e.g.: ZERO_APP_PUBLICATIONS=zero_data1,zero_data2. flag: --app-publications env: ZERO_APP_PUBLICATIONS default: _{app-id}_public_0 Auth Revalidate Interval Seconds How often zero-cache re-checks that each live connection is still authorized to use your /query endpoint. On each interval, zero-cache sends a lightweight validation request using that connection's current auth context, such as forwarded cookies or an opaque auth token. If your query endpoint rejects that auth with a 401/403, the connection is disconnected. Use this to bound how long already-open connections can continue after logout, session expiry, token revocation, or other server-side auth changes that happen without a reconnect. Lower values enforce auth changes faster, but send more validation requests to /query. flag: --auth-revalidate-interval-seconds env: ZERO_AUTH_REVALIDATE_INTERVAL_SECONDS default: unset Auth Retransform Interval Seconds How often zero-cache refreshes a client group's synced or named query transformations using one validated connection from that group. This re-runs auth-sensitive query expansion even when the query set itself has not changed. It is useful when your query endpoint generates different ZQL based on current auth or server-side session state, such as roles, organization membership, feature flags, or other permissions-derived context. Use this to bound how long a client group can keep using stale auth-derived query shapes after backend auth state changes. Lower values pick up those changes faster, but do more /query transform work. If clients already call updateAuth whenever auth changes, this mainly serves as a background safety net for out-of-band auth changes. flag: --auth-retransform-interval-seconds env: ZERO_AUTH_RETRANSFORM_INTERVAL_SECONDS default: unset Auto Reset Automatically wipe and resync the replica when replication is halted. This situation can occur for configurations in which the upstream database provider prohibits event trigger creation, preventing the zero-cache from being able to correctly replicate schema changes. For such configurations, an upstream schema change will instead result in halting replication with an error indicating that the replica needs to be reset. When auto-reset is enabled, zero-cache will respond to such situations by shutting down, and when restarted, resetting the replica and all synced clients. This is a heavy-weight operation and can result in user-visible slowness or downtime if compute resources are scarce. flag: --auto-reset env: ZERO_AUTO_RESET default: true Change DB The Postgres database used to store recent replication log entries, in order to sync multiple view-syncers without requiring multiple replication slots on the upstream database. If unspecified, the upstream-db will be used. flag: --change-db env: ZERO_CHANGE_DB Change Max Connections The maximum number of connections to open to the change database. This is used by the change-streamer for catching up zero-cache replication subscriptions. flag: --change-max-conns env: ZERO_CHANGE_MAX_CONNS default: 5 Change Streamer Back Pressure Limit Heap Proportion The percentage of --max-old-space-size to use as a buffer for absorbing replication stream spikes. When the estimated amount of queued data exceeds this threshold, back pressure is applied to the replication stream, delaying downstream sync as a result. The threshold was determined empirically with load testing. Higher thresholds have resulted in OOMs. Note also that the byte-counting logic in the queue is strictly an underestimate of actual memory usage (but importantly, proportionally correct), so the queue is actually using more than what this proportion suggests. This parameter is exported as an emergency knob to reduce the size of the buffer in the event that the server OOMs from back pressure. Resist the urge to increase this proportion, as it is mainly useful for absorbing periodic spikes and does not meaningfully affect steady-state replication throughput; the latter is determined by other factors such as object serialization and PG throughput. In other words, the back pressure limit does not constrain replication throughput; rather, it protects the system when the upstream throughput exceeds the downstream throughput. flag: --change-streamer-back-pressure-limit-heap-proportion env: ZERO_CHANGE_STREAMER_BACK_PRESSURE_LIMIT_HEAP_PROPORTION default: 0.04 Change Streamer Flow Control Consensus Padding Seconds During periodic flow control checks (every 64kb), this is the amount of time to wait after the majority of subscribers have acked, after which replication continues even if some subscribers have yet to ack. This is not a timeout for the entire send; it starts only after the majority of receivers have acked. This allows a bounded amount of time for backlogged subscribers to catch up on each flush without forcing all subscribers to wait for the entire backlog to be processed. It is also useful for mitigating the effect of unresponsive subscribers due to severed WebSocket connections until liveness checks disconnect them. Set this to a negative number to disable early flow control releases. flag: --change-streamer-flow-control-consensus-padding-seconds env: ZERO_CHANGE_STREAMER_FLOW_CONTROL_CONSENSUS_PADDING_SECONDS default: 1 Change Streamer Mode The mode for running or connecting to the change-streamer: dedicated: runs the change-streamer and shuts down when another change-streamer takes over the replication slot. This is appropriate in a single-node configuration, or for the replication-manager in a multi-node configuration. discover: connects to the change-streamer as internally advertised in the change-db. This is appropriate for the view-syncers in a multi-node setup. This may not work in all networking configurations (e.g., some private networking or port forwarding setups). Using ZERO_CHANGE_STREAMER_URI with an explicit routable hostname is recommended instead. This option is ignored if ZERO_CHANGE_STREAMER_URI is set. flag: --change-streamer-mode env: ZERO_CHANGE_STREAMER_MODE default: dedicated Change Streamer Port The port on which the change-streamer runs. This is an internal protocol between the replication-manager and view-syncers, which runs in the same process tree in local development or a single-node configuration. If unspecified, defaults to --port + 1. flag: --change-streamer-port env: ZERO_CHANGE_STREAMER_PORT default: --port + 1 Change Streamer Startup Delay (ms) The delay to wait before the change-streamer takes over the replication stream (i.e. the handoff during replication-manager updates), to allow load balancers to register the task as healthy based on healthcheck parameters. If a change stream request is received during this interval, the delay will be canceled and the takeover will happen immediately, since the incoming request indicates that the task is registered as a target. flag: --change-streamer-startup-delay-ms env: ZERO_CHANGE_STREAMER_STARTUP_DELAY_MS default: 15000 Change Streamer URI When set, connects to the change-streamer at the given URI. In a multi-node setup, this should be specified in view-syncer options, pointing to the replication-manager URI, which runs a change-streamer on port 4849. flag: --change-streamer-uri env: ZERO_CHANGE_STREAMER_URI CVR DB The Postgres database used to store CVRs. CVRs (client view records) keep track of the data synced to clients in order to determine the diff to send on reconnect. If unspecified, the upstream-db will be used. flag: --cvr-db env: ZERO_CVR_DB CVR Garbage Collection Inactivity Threshold Hours The duration after which an inactive CVR is eligible for garbage collection. Garbage collection is incremental and periodic, so eligible CVRs are not necessarily purged immediately. flag: --cvr-garbage-collection-inactivity-threshold-hours env: ZERO_CVR_GARBAGE_COLLECTION_INACTIVITY_THRESHOLD_HOURS default: 48 CVR Garbage Collection Initial Batch Size The initial number of CVRs to purge per garbage collection interval. This number is increased linearly if the rate of new CVRs exceeds the rate of purged CVRs, in order to reach a steady state. Setting this to 0 effectively disables CVR garbage collection. flag: --cvr-garbage-collection-initial-batch-size env: ZERO_CVR_GARBAGE_COLLECTION_INITIAL_BATCH_SIZE default: 25 CVR Garbage Collection Initial Interval Seconds The initial interval at which to check and garbage collect inactive CVRs. This interval is increased exponentially (up to 16 minutes) when there is nothing to purge. flag: --cvr-garbage-collection-initial-interval-seconds env: ZERO_CVR_GARBAGE_COLLECTION_INITIAL_INTERVAL_SECONDS default: 60 CVR Max Connections The maximum number of connections to open to the CVR database. This is divided evenly amongst sync workers. Note that this number must allow for at least one connection per sync worker, or zero-cache will fail to start. See num-sync-workers. flag: --cvr-max-conns env: ZERO_CVR_MAX_CONNS default: 30 Enable Query Planner Enable the query planner for optimizing ZQL queries. The query planner analyzes and optimizes query execution by determining the most efficient join strategies. You can disable the planner if it is picking bad strategies. flag: --enable-query-planner env: ZERO_ENABLE_QUERY_PLANNER default: true Enable CRUD Mutations Enables support for legacy CRUD mutations. When this is false, view-syncers do not connect to the upstream database for CRUD writes, and push messages with CRUD mutations return an error response. flag: --enable-crud-mutations env: ZERO_ENABLE_CRUD_MUTATIONS default: true Enable Telemetry Zero collects anonymous telemetry data to help us understand usage. We collect: Zero version Uptime General machine information, like the number of CPUs, OS, CI/CD environment, etc. Information about usage, such as number of queries or mutations processed per hour. This is completely optional and can be disabled at any time. You can also opt-out by setting DO_NOT_TRACK=1. flag: --enable-telemetry env: ZERO_ENABLE_TELEMETRY default: true Initial Sync Table Copy Workers The number of parallel workers used to copy tables during initial sync. Each worker uses a database connection, copies a single table at a time, and buffers up to (approximately) 10 MB of table data in memory during initial sync. Increasing the number of workers may improve initial sync speed; however, local disk throughput (IOPS), upstream CPU, and network bandwidth may also be bottlenecks. flag: --initial-sync-table-copy-workers env: ZERO_INITIAL_SYNC_TABLE_COPY_WORKERS default: 5 Lazy Startup Delay starting the majority of zero-cache until first request. This is mainly intended to avoid connecting to Postgres replication stream until the first request is received, which can be useful i.e., for preview instances. Currently only supported in single-node mode. flag: --lazy-startup env: ZERO_LAZY_STARTUP default: false Litestream Backup URL The location of the litestream backup, usually an s3:// URL. This is only consulted by the replication-manager. view-syncers receive this information from the replication-manager. In multi-node deployments, this is required on the replication-manager so view-syncers can reserve snapshots; in single-node deployments it is optional. flag: --litestream-backup-url env: ZERO_LITESTREAM_BACKUP_URL Litestream Endpoint The S3-compatible endpoint URL to use for the litestream backup. This is only required for non-AWS services. The replication-manager and view-syncers must have the same endpoint. For example, to use Cloudflare R2: https://.r2.cloudflarestorage.com. flag: --litestream-endpoint env: ZERO_LITESTREAM_ENDPOINT Litestream Checkpoint Threshold MB The size of the WAL file at which to perform an SQlite checkpoint to apply the writes in the WAL to the main database file. Each checkpoint creates a new WAL segment file that will be backed up by litestream. Smaller thresholds may improve read performance, at the expense of creating more files to download when restoring the replica from the backup. flag: --litestream-checkpoint-threshold-mb env: ZERO_LITESTREAM_CHECKPOINT_THRESHOLD_MB default: 40 Litestream Config Path Path to the litestream yaml config file. zero-cache will run this with its environment variables, which can be referenced in the file via ${ENV} substitution, for example: ZERO_REPLICA_FILE for the db Path ZERO_LITESTREAM_BACKUP_LOCATION for the db replica url ZERO_LITESTREAM_LOG_LEVEL for the log Level ZERO_LOG_FORMAT for the log type flag: --litestream-config-path env: ZERO_LITESTREAM_CONFIG_PATH default: ./src/services/litestream/config.yml Litestream Executable Path to the litestream executable. This must be built from the rocicorp/litestream fork. This option has no effect if litestream-backup-url is unspecified. flag: --litestream-executable env: ZERO_LITESTREAM_EXECUTABLE Litestream Incremental Backup Interval Minutes The interval between incremental backups of the replica. Shorter intervals reduce the amount of change history that needs to be replayed when catching up a new view-syncer, at the expense of increasing the number of files needed to download for the initial litestream restore. flag: --litestream-incremental-backup-interval-minutes env: ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES default: 15 Litestream Maximum Checkpoint Page Count The WAL page count at which SQLite performs a RESTART checkpoint, which blocks writers until complete. Defaults to minCheckpointPageCount * 10. Set to 0 to disable RESTART checkpoints entirely. flag: --litestream-max-checkpoint-page-count env: ZERO_LITESTREAM_MAX_CHECKPOINT_PAGE_COUNT default: minCheckpointPageCount * 10 Litestream Minimum Checkpoint Page Count The WAL page count at which SQLite attempts a PASSIVE checkpoint, which transfers pages to the main database file without blocking writers. Defaults to checkpointThresholdMB * 250 (since SQLite page size is 4KB). flag: --litestream-min-checkpoint-page-count env: ZERO_LITESTREAM_MIN_CHECKPOINT_PAGE_COUNT default: checkpointThresholdMB * 250 Litestream Multipart Concurrency The number of parts (of size --litestream-multipart-size bytes) to upload or download in parallel when backing up or restoring the snapshot. flag: --litestream-multipart-concurrency env: ZERO_LITESTREAM_MULTIPART_CONCURRENCY default: 48 Litestream Multipart Size The size of each part when uploading or downloading the snapshot with --litestream-multipart-concurrency. Note that up to concurrency * size bytes of memory are used when backing up or restoring the snapshot. flag: --litestream-multipart-size env: ZERO_LITESTREAM_MULTIPART_SIZE default: 16777216 (16 MiB) Litestream Log Level flag: --litestream-log-level env: ZERO_LITESTREAM_LOG_LEVEL default: warn values: debug, info, warn, error Litestream Port Port on which litestream exports metrics, used to determine the replication watermark up to which it is safe to purge change log records. flag: --litestream-port env: ZERO_LITESTREAM_PORT default: --port + 2 Litestream Region The AWS region for the litestream backup bucket. Required for non-standard AWS partitions (e.g. GovCloud us-gov-west-1) where Litestream cannot auto-detect the region. The replication-manager and view-syncers must have the same region. flag: --litestream-region env: ZERO_LITESTREAM_REGION Litestream Restore Parallelism The number of WAL files to download in parallel when performing the initial restore of the replica from the backup. flag: --litestream-restore-parallelism env: ZERO_LITESTREAM_RESTORE_PARALLELISM default: 48 Litestream Snapshot Backup Interval Hours The interval between snapshot backups of the replica. Snapshot backups make a full copy of the database to a new litestream generation. This improves restore time at the expense of bandwidth. Applications with a large database and low write rate can increase this interval to reduce network usage for backups (litestream defaults to 24 hours). flag: --litestream-snapshot-backup-interval-hours env: ZERO_LITESTREAM_SNAPSHOT_BACKUP_INTERVAL_HOURS default: 12 Log Format Use text for developer-friendly console logging and json for consumption by structured-logging services. flag: --log-format env: ZERO_LOG_FORMAT default: \"text\" values: text, json Log IVM Sampling How often to collect IVM metrics. 1 out of N requests will be sampled where N is this value. flag: --log-ivm-sampling env: ZERO_LOG_IVM_SAMPLING default: 5000 Log Level Sets the logging level for the application. flag: --log-level env: ZERO_LOG_LEVEL default: \"info\" values: debug, info, warn, error Log Slow Hydrate Threshold The number of milliseconds a query hydration must take to print a slow warning. flag: --log-slow-hydrate-threshold env: ZERO_LOG_SLOW_HYDRATE_THRESHOLD default: 100 Log Slow Row Threshold The number of ms a row must take to fetch from table-source before it is considered slow. flag: --log-slow-row-threshold env: ZERO_LOG_SLOW_ROW_THRESHOLD default: 2 Mutate API Key An optional secret used to authorize zero-cache to call the API server handling writes. This is sent from zero-cache to your mutate endpoint in an X-Api-Key header. flag: --mutate-api-key env: ZERO_MUTATE_API_KEY Mutate Allowed Client Headers Comma-separated allowlist of client-provided custom headers to forward to your mutate endpoint. Header names are matched case-insensitively. By default, no client-provided custom headers are forwarded. flag: --mutate-allowed-client-headers env: ZERO_MUTATE_ALLOWED_CLIENT_HEADERS default: none Mutate Allowed Request Headers Comma-separated allowlist of HTTP headers from the request that opened the WebSocket to forward to your mutate endpoint. Use this for proxy- or load-balancer-injected headers such as x-forwarded-for or cf-ray. Unlike mutate allowed client headers, these values come from the request that established the connection. Header names are matched case-insensitively. Values are retained for the WebSocket's lifetime, so clients must reconnect to receive changes. The allowlist does not verify the header source - only allow headers that a trusted proxy overwrites or removes from untrusted requests. No request headers are forwarded by default. flag: --mutate-allowed-request-headers env: ZERO_MUTATE_ALLOWED_REQUEST_HEADERS default: none Mutate Forward Cookies If true, zero-cache will forward cookies from the request to zero-cache to your mutate endpoint. This is useful for passing authentication cookies to the API server. If false, cookies are not forwarded. flag: --mutate-forward-cookies env: ZERO_MUTATE_FORWARD_COOKIES default: false Mutate URL The URL of the API server to which zero-cache will push mutations. URLs are matched using URLPattern, a standard Web API. Pattern syntax (similar to Express routes): Exact URL match: \"https://api.example.com/mutate\" Any subdomain using wildcard: \"https://*.example.com/mutate\" Multiple subdomain levels: \"https://*.*.example.com/mutate\" Any path under a domain: \"https://api.example.com/*\" Named path parameters: \"https://api.example.com/:version/mutate\" Matches https://api.example.com/v1/mutate, https://api.example.com/v2/mutate, etc. Advanced patterns: Optional path segments: \"https://api.example.com/:path?\" Regex in segments (for specific patterns): \"https://api.example.com/:version(v\\\\d+)/mutate\" matches only v followed by digits. Multiple patterns can be specified, for example: https://api1.example.com/mutate,https://api2.example.com/mutate Query parameters and URL fragments (#) are ignored during matching. See URLPattern for full syntax. flag: --mutate-url env: ZERO_MUTATE_URL Number of Sync Workers The number of processes to use for view syncing. Leave this unset to use max(1, availableParallelism() - 1), reserving one core for the replicator. If set to 0, the server runs without sync workers, which is the configuration for running the replication-manager in multi-node deployments. flag: --num-sync-workers env: ZERO_NUM_SYNC_WORKERS Per User Mutation Limit Max The maximum mutations per user within the specified windowMs. flag: --per-user-mutation-limit-max env: ZERO_PER_USER_MUTATION_LIMIT_MAX Per User Mutation Limit Window (ms) The sliding window over which the perUserMutationLimitMax is enforced. flag: --per-user-mutation-limit-window-ms env: ZERO_PER_USER_MUTATION_LIMIT_WINDOW_MS default: 60000 PG Replication Slot Failover For upstream Postgres 17+, creates replication slots with the failover flag enabled so they can be synchronized to a standby and survive a failover. This requires additional Postgres-side configuration on your provider; see High Availability. Has no effect on Postgres versions before 17. flag: --upstream-pg-replication-slot-failover env: ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER default: false Port The port for sync connections. flag: --port env: ZERO_PORT default: 4848 Query API Key An optional secret used to authorize zero-cache to call the API server handling queries. This is sent from zero-cache to your query endpoint in an X-Api-Key header. flag: --query-api-key env: ZERO_QUERY_API_KEY Query Allowed Client Headers Comma-separated allowlist of client-provided custom headers to forward to your query endpoint. Header names are matched case-insensitively. By default, no client-provided custom headers are forwarded. flag: --query-allowed-client-headers env: ZERO_QUERY_ALLOWED_CLIENT_HEADERS default: none Query Allowed Request Headers Comma-separated allowlist of HTTP headers from the request that opened the WebSocket to forward to your query endpoint. Use this for proxy- or load-balancer-injected headers such as x-forwarded-for or cf-ray. Unlike query allowed client headers, these values come from the request that established the connection. Header names are matched case-insensitively. Values are retained for the WebSocket's lifetime, so clients must reconnect to receive changes. The allowlist does not verify the header source - only allow headers that a trusted proxy overwrites or removes from untrusted requests. No request headers are forwarded by default. flag: --query-allowed-request-headers env: ZERO_QUERY_ALLOWED_REQUEST_HEADERS default: none Query Forward Cookies If true, zero-cache will forward cookies from the request to zero-cache to your query endpoint. This is useful for passing authentication cookies to the API server. If false, cookies are not forwarded. flag: --query-forward-cookies env: ZERO_QUERY_FORWARD_COOKIES default: false Query Hydration Stats Track and log the number of rows considered by query hydrations which take longer than log-slow-hydrate-threshold milliseconds. This is useful for debugging and performance tuning. flag: --query-hydration-stats env: ZERO_QUERY_HYDRATION_STATS Query URL The URL of the API server to which zero-cache will send synced queries. URLs are matched using URLPattern, a standard Web API. Pattern syntax (similar to Express routes): Exact URL match: \"https://api.example.com/query\" Any subdomain using wildcard: \"https://*.example.com/query\" Multiple subdomain levels: \"https://*.*.example.com/query\" Any path under a domain: \"https://api.example.com/*\" Named path parameters: \"https://api.example.com/:version/query\" Matches https://api.example.com/v1/query, https://api.example.com/v2/query, etc. Advanced patterns: Optional path segments: \"https://api.example.com/:path?\" Regex in segments (for specific patterns): \"https://api.example.com/:version(v\\\\d+)/query\" matches only v followed by digits. Multiple patterns can be specified, for example: https://api1.example.com/query,https://api2.example.com/query Query parameters and URL fragments (#) are ignored during matching. See URLPattern for full syntax. flag: --query-url env: ZERO_QUERY_URL Replica File File path to the SQLite replica that zero-cache maintains. This can be lost, but if it is, zero-cache will have to re-replicate next time it starts up. flag: --replica-file env: ZERO_REPLICA_FILE default: \"zero.db\" Replica Vacuum Interval Hours Performs a VACUUM at server startup if the specified number of hours has elapsed since the last VACUUM (or initial-sync). The VACUUM operation is heavyweight and requires double the size of the db in disk space. If unspecified, VACUUM operations are not performed. flag: --replica-vacuum-interval-hours env: ZERO_REPLICA_VACUUM_INTERVAL_HOURS Replication Lag Report Interval (ms) The minimum interval at which replication lag reports are written upstream and reported via the zero.replication.total_lag OpenTelemetry metric. If an expected report is not received before the next interval, Zero emits a new report and increments zero.replication.lag_report_retries. This feature requires write access to upstream Postgres (uses pg_logical_emit_message()). For PostgreSQL 17+, lag measurements accurately reflect committed write latency (single-digit milliseconds). For PostgreSQL 16 and earlier, measurements may appear 50-100ms longer due to flush behavior. A negative or 0 value disables lag reporting. Even if otel is not enabled, info and warn-level logs are emitted for large lag values. flag: --replication-lag-report-interval-ms env: ZERO_REPLICATION_LAG_REPORT_INTERVAL_MS default: 30_000 Server Version The version string outputted to logs when the server starts up. flag: --server-version env: ZERO_SERVER_VERSION Shadow Sync Enabled Periodically exercises the initial-sync code path against a sample of rows from every published table, writing to a throwaway SQLite database. This acts as a canary: if the real initial-sync path breaks because of schema drift, Postgres version quirks, or another full-resync issue, the shadow run fails before a customer actually needs a full reset. flag: --shadow-sync-enabled env: ZERO_SHADOW_SYNC_ENABLED default: false Shadow Sync Interval Hours The interval between shadow initial-sync runs, in hours. The first run fires within [2/3, 1) of this interval after startup, so the canary completes at least once per task lifetime while still jittering fleet restarts. flag: --shadow-sync-interval-hours env: ZERO_SHADOW_SYNC_INTERVAL_HOURS default: 12 Shadow Sync Sample Rate The Bernoulli sampling rate for each table, where 0 < rate <= 1. A value of 1 disables sampling and copies all rows, still subject to --shadow-sync-max-rows-per-table. flag: --shadow-sync-sample-rate env: ZERO_SHADOW_SYNC_SAMPLE_RATE default: 0.1 Shadow Sync Max Rows Per Table The hard upper bound on rows copied per table per shadow run. This guards against unexpectedly large tables consuming too much disk or upstream bandwidth. flag: --shadow-sync-max-rows-per-table env: ZERO_SHADOW_SYNC_MAX_ROWS_PER_TABLE default: 10000 Storage DB Temp Dir Temporary directory for IVM operator storage. Leave unset to use os.tmpdir(). flag: --storage-db-tmp-dir env: ZERO_STORAGE_DB_TMP_DIR Task ID Globally unique identifier for the zero-cache instance. Setting this to a platform specific task identifier can be useful for debugging. If unspecified, zero-cache will attempt to extract the TaskARN if run from within an AWS ECS container, and otherwise use a random string. flag: --task-id env: ZERO_TASK_ID Upstream Max Connections The maximum number of connections to open to the upstream database for committing mutations. This is divided evenly amongst sync workers. In addition to this number, zero-cache uses one connection for the replication stream. Note that this number must allow for at least one connection per sync worker, or zero-cache will fail to start. See num-sync-workers. flag: --upstream-max-conns env: ZERO_UPSTREAM_MAX_CONNS default: 20 Upstream PG Replication Slot Failover For upstream PostgreSQL 17 and later, create replication slots with the failover parameter set to true to enable slot synchronization and failover. Additional Postgres-level configuration is required when enabling this option. This option has no effect for PostgreSQL versions before 17. See the PostgreSQL docs for details: https://www.postgresql.org/docs/current/logicaldecoding-explanation.html#LOGICALDECODING-REPLICATION-SLOTS-SYNCHRONIZATION flag: --upstream-pg-replication-slot-failover env: ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER default: false Websocket Compression Enable WebSocket per-message deflate compression. Compression can reduce bandwidth usage for sync traffic but increases CPU usage on both client and server. Disabled by default. See: https://github.com/websockets/ws#websocket-compression flag: --websocket-compression env: ZERO_WEBSOCKET_COMPRESSION default: false Websocket Compression Options JSON string containing WebSocket compression options. Only used if websocket-compression is enabled. Example: {\"zlibDeflateOptions\":{\"level\":3},\"threshold\":1024}. See https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback for available options. flag: --websocket-compression-options env: ZERO_WEBSOCKET_COMPRESSION_OPTIONS Websocket Max Payload Bytes Maximum size of incoming WebSocket messages in bytes. Messages exceeding this limit are rejected before parsing. flag: --websocket-max-payload-bytes env: ZERO_WEBSOCKET_MAX_PAYLOAD_BYTES default: 10485760 (10 MiB) Yield Threshold (ms) The maximum amount of time in milliseconds that a sync worker will spend in IVM (processing query hydration and advancement) before yielding to the event loop. Lower values increase responsiveness and fairness at the cost of reduced throughput. flag: --yield-threshold-ms env: ZERO_YIELD_THRESHOLD_MS default: 10", "kind": "section" }, { - "id": "577-zero-cache-config#app-id", + "id": "585-zero-cache-config#app-id", "title": "zero-cache Config", "searchTitle": "App ID", "sectionTitle": "App ID", @@ -8084,7 +8192,7 @@ "kind": "section" }, { - "id": "578-zero-cache-config#app-publications", + "id": "586-zero-cache-config#app-publications", "title": "zero-cache Config", "searchTitle": "App Publications", "sectionTitle": "App Publications", @@ -8094,7 +8202,7 @@ "kind": "section" }, { - "id": "579-zero-cache-config#auth-revalidate-interval-seconds", + "id": "587-zero-cache-config#auth-revalidate-interval-seconds", "title": "zero-cache Config", "searchTitle": "Auth Revalidate Interval Seconds", "sectionTitle": "Auth Revalidate Interval Seconds", @@ -8104,7 +8212,7 @@ "kind": "section" }, { - "id": "580-zero-cache-config#auth-retransform-interval-seconds", + "id": "588-zero-cache-config#auth-retransform-interval-seconds", "title": "zero-cache Config", "searchTitle": "Auth Retransform Interval Seconds", "sectionTitle": "Auth Retransform Interval Seconds", @@ -8114,7 +8222,7 @@ "kind": "section" }, { - "id": "581-zero-cache-config#auto-reset", + "id": "589-zero-cache-config#auto-reset", "title": "zero-cache Config", "searchTitle": "Auto Reset", "sectionTitle": "Auto Reset", @@ -8124,7 +8232,7 @@ "kind": "section" }, { - "id": "582-zero-cache-config#change-db", + "id": "590-zero-cache-config#change-db", "title": "zero-cache Config", "searchTitle": "Change DB", "sectionTitle": "Change DB", @@ -8134,7 +8242,7 @@ "kind": "section" }, { - "id": "583-zero-cache-config#change-max-connections", + "id": "591-zero-cache-config#change-max-connections", "title": "zero-cache Config", "searchTitle": "Change Max Connections", "sectionTitle": "Change Max Connections", @@ -8144,7 +8252,7 @@ "kind": "section" }, { - "id": "584-zero-cache-config#change-streamer-back-pressure-limit-heap-proportion", + "id": "592-zero-cache-config#change-streamer-back-pressure-limit-heap-proportion", "title": "zero-cache Config", "searchTitle": "Change Streamer Back Pressure Limit Heap Proportion", "sectionTitle": "Change Streamer Back Pressure Limit Heap Proportion", @@ -8154,7 +8262,7 @@ "kind": "section" }, { - "id": "585-zero-cache-config#change-streamer-flow-control-consensus-padding-seconds", + "id": "593-zero-cache-config#change-streamer-flow-control-consensus-padding-seconds", "title": "zero-cache Config", "searchTitle": "Change Streamer Flow Control Consensus Padding Seconds", "sectionTitle": "Change Streamer Flow Control Consensus Padding Seconds", @@ -8164,7 +8272,7 @@ "kind": "section" }, { - "id": "586-zero-cache-config#change-streamer-mode", + "id": "594-zero-cache-config#change-streamer-mode", "title": "zero-cache Config", "searchTitle": "Change Streamer Mode", "sectionTitle": "Change Streamer Mode", @@ -8174,7 +8282,7 @@ "kind": "section" }, { - "id": "587-zero-cache-config#change-streamer-port", + "id": "595-zero-cache-config#change-streamer-port", "title": "zero-cache Config", "searchTitle": "Change Streamer Port", "sectionTitle": "Change Streamer Port", @@ -8184,7 +8292,7 @@ "kind": "section" }, { - "id": "588-zero-cache-config#change-streamer-startup-delay-ms", + "id": "596-zero-cache-config#change-streamer-startup-delay-ms", "title": "zero-cache Config", "searchTitle": "Change Streamer Startup Delay (ms)", "sectionTitle": "Change Streamer Startup Delay (ms)", @@ -8194,7 +8302,7 @@ "kind": "section" }, { - "id": "589-zero-cache-config#change-streamer-uri", + "id": "597-zero-cache-config#change-streamer-uri", "title": "zero-cache Config", "searchTitle": "Change Streamer URI", "sectionTitle": "Change Streamer URI", @@ -8204,7 +8312,7 @@ "kind": "section" }, { - "id": "590-zero-cache-config#cvr-db", + "id": "598-zero-cache-config#cvr-db", "title": "zero-cache Config", "searchTitle": "CVR DB", "sectionTitle": "CVR DB", @@ -8214,7 +8322,7 @@ "kind": "section" }, { - "id": "591-zero-cache-config#cvr-garbage-collection-inactivity-threshold-hours", + "id": "599-zero-cache-config#cvr-garbage-collection-inactivity-threshold-hours", "title": "zero-cache Config", "searchTitle": "CVR Garbage Collection Inactivity Threshold Hours", "sectionTitle": "CVR Garbage Collection Inactivity Threshold Hours", @@ -8224,7 +8332,7 @@ "kind": "section" }, { - "id": "592-zero-cache-config#cvr-garbage-collection-initial-batch-size", + "id": "600-zero-cache-config#cvr-garbage-collection-initial-batch-size", "title": "zero-cache Config", "searchTitle": "CVR Garbage Collection Initial Batch Size", "sectionTitle": "CVR Garbage Collection Initial Batch Size", @@ -8234,7 +8342,7 @@ "kind": "section" }, { - "id": "593-zero-cache-config#cvr-garbage-collection-initial-interval-seconds", + "id": "601-zero-cache-config#cvr-garbage-collection-initial-interval-seconds", "title": "zero-cache Config", "searchTitle": "CVR Garbage Collection Initial Interval Seconds", "sectionTitle": "CVR Garbage Collection Initial Interval Seconds", @@ -8244,7 +8352,7 @@ "kind": "section" }, { - "id": "594-zero-cache-config#cvr-max-connections", + "id": "602-zero-cache-config#cvr-max-connections", "title": "zero-cache Config", "searchTitle": "CVR Max Connections", "sectionTitle": "CVR Max Connections", @@ -8254,7 +8362,7 @@ "kind": "section" }, { - "id": "595-zero-cache-config#enable-query-planner", + "id": "603-zero-cache-config#enable-query-planner", "title": "zero-cache Config", "searchTitle": "Enable Query Planner", "sectionTitle": "Enable Query Planner", @@ -8264,7 +8372,7 @@ "kind": "section" }, { - "id": "596-zero-cache-config#enable-crud-mutations", + "id": "604-zero-cache-config#enable-crud-mutations", "title": "zero-cache Config", "searchTitle": "Enable CRUD Mutations", "sectionTitle": "Enable CRUD Mutations", @@ -8274,7 +8382,7 @@ "kind": "section" }, { - "id": "597-zero-cache-config#enable-telemetry", + "id": "605-zero-cache-config#enable-telemetry", "title": "zero-cache Config", "searchTitle": "Enable Telemetry", "sectionTitle": "Enable Telemetry", @@ -8284,7 +8392,7 @@ "kind": "section" }, { - "id": "598-zero-cache-config#initial-sync-table-copy-workers", + "id": "606-zero-cache-config#initial-sync-table-copy-workers", "title": "zero-cache Config", "searchTitle": "Initial Sync Table Copy Workers", "sectionTitle": "Initial Sync Table Copy Workers", @@ -8294,7 +8402,7 @@ "kind": "section" }, { - "id": "599-zero-cache-config#lazy-startup", + "id": "607-zero-cache-config#lazy-startup", "title": "zero-cache Config", "searchTitle": "Lazy Startup", "sectionTitle": "Lazy Startup", @@ -8304,7 +8412,7 @@ "kind": "section" }, { - "id": "600-zero-cache-config#litestream-backup-url", + "id": "608-zero-cache-config#litestream-backup-url", "title": "zero-cache Config", "searchTitle": "Litestream Backup URL", "sectionTitle": "Litestream Backup URL", @@ -8314,7 +8422,7 @@ "kind": "section" }, { - "id": "601-zero-cache-config#litestream-endpoint", + "id": "609-zero-cache-config#litestream-endpoint", "title": "zero-cache Config", "searchTitle": "Litestream Endpoint", "sectionTitle": "Litestream Endpoint", @@ -8324,7 +8432,7 @@ "kind": "section" }, { - "id": "602-zero-cache-config#litestream-checkpoint-threshold-mb", + "id": "610-zero-cache-config#litestream-checkpoint-threshold-mb", "title": "zero-cache Config", "searchTitle": "Litestream Checkpoint Threshold MB", "sectionTitle": "Litestream Checkpoint Threshold MB", @@ -8334,7 +8442,7 @@ "kind": "section" }, { - "id": "603-zero-cache-config#litestream-config-path", + "id": "611-zero-cache-config#litestream-config-path", "title": "zero-cache Config", "searchTitle": "Litestream Config Path", "sectionTitle": "Litestream Config Path", @@ -8344,7 +8452,7 @@ "kind": "section" }, { - "id": "604-zero-cache-config#litestream-executable", + "id": "612-zero-cache-config#litestream-executable", "title": "zero-cache Config", "searchTitle": "Litestream Executable", "sectionTitle": "Litestream Executable", @@ -8354,7 +8462,7 @@ "kind": "section" }, { - "id": "605-zero-cache-config#litestream-incremental-backup-interval-minutes", + "id": "613-zero-cache-config#litestream-incremental-backup-interval-minutes", "title": "zero-cache Config", "searchTitle": "Litestream Incremental Backup Interval Minutes", "sectionTitle": "Litestream Incremental Backup Interval Minutes", @@ -8364,7 +8472,7 @@ "kind": "section" }, { - "id": "606-zero-cache-config#litestream-maximum-checkpoint-page-count", + "id": "614-zero-cache-config#litestream-maximum-checkpoint-page-count", "title": "zero-cache Config", "searchTitle": "Litestream Maximum Checkpoint Page Count", "sectionTitle": "Litestream Maximum Checkpoint Page Count", @@ -8374,7 +8482,7 @@ "kind": "section" }, { - "id": "607-zero-cache-config#litestream-minimum-checkpoint-page-count", + "id": "615-zero-cache-config#litestream-minimum-checkpoint-page-count", "title": "zero-cache Config", "searchTitle": "Litestream Minimum Checkpoint Page Count", "sectionTitle": "Litestream Minimum Checkpoint Page Count", @@ -8384,7 +8492,7 @@ "kind": "section" }, { - "id": "608-zero-cache-config#litestream-multipart-concurrency", + "id": "616-zero-cache-config#litestream-multipart-concurrency", "title": "zero-cache Config", "searchTitle": "Litestream Multipart Concurrency", "sectionTitle": "Litestream Multipart Concurrency", @@ -8394,7 +8502,7 @@ "kind": "section" }, { - "id": "609-zero-cache-config#litestream-multipart-size", + "id": "617-zero-cache-config#litestream-multipart-size", "title": "zero-cache Config", "searchTitle": "Litestream Multipart Size", "sectionTitle": "Litestream Multipart Size", @@ -8404,7 +8512,7 @@ "kind": "section" }, { - "id": "610-zero-cache-config#litestream-log-level", + "id": "618-zero-cache-config#litestream-log-level", "title": "zero-cache Config", "searchTitle": "Litestream Log Level", "sectionTitle": "Litestream Log Level", @@ -8414,7 +8522,7 @@ "kind": "section" }, { - "id": "611-zero-cache-config#litestream-port", + "id": "619-zero-cache-config#litestream-port", "title": "zero-cache Config", "searchTitle": "Litestream Port", "sectionTitle": "Litestream Port", @@ -8424,7 +8532,7 @@ "kind": "section" }, { - "id": "612-zero-cache-config#litestream-region", + "id": "620-zero-cache-config#litestream-region", "title": "zero-cache Config", "searchTitle": "Litestream Region", "sectionTitle": "Litestream Region", @@ -8434,7 +8542,7 @@ "kind": "section" }, { - "id": "613-zero-cache-config#litestream-restore-parallelism", + "id": "621-zero-cache-config#litestream-restore-parallelism", "title": "zero-cache Config", "searchTitle": "Litestream Restore Parallelism", "sectionTitle": "Litestream Restore Parallelism", @@ -8444,7 +8552,7 @@ "kind": "section" }, { - "id": "614-zero-cache-config#litestream-snapshot-backup-interval-hours", + "id": "622-zero-cache-config#litestream-snapshot-backup-interval-hours", "title": "zero-cache Config", "searchTitle": "Litestream Snapshot Backup Interval Hours", "sectionTitle": "Litestream Snapshot Backup Interval Hours", @@ -8454,7 +8562,7 @@ "kind": "section" }, { - "id": "615-zero-cache-config#log-format", + "id": "623-zero-cache-config#log-format", "title": "zero-cache Config", "searchTitle": "Log Format", "sectionTitle": "Log Format", @@ -8464,7 +8572,7 @@ "kind": "section" }, { - "id": "616-zero-cache-config#log-ivm-sampling", + "id": "624-zero-cache-config#log-ivm-sampling", "title": "zero-cache Config", "searchTitle": "Log IVM Sampling", "sectionTitle": "Log IVM Sampling", @@ -8474,7 +8582,7 @@ "kind": "section" }, { - "id": "617-zero-cache-config#log-level", + "id": "625-zero-cache-config#log-level", "title": "zero-cache Config", "searchTitle": "Log Level", "sectionTitle": "Log Level", @@ -8484,7 +8592,7 @@ "kind": "section" }, { - "id": "618-zero-cache-config#log-slow-hydrate-threshold", + "id": "626-zero-cache-config#log-slow-hydrate-threshold", "title": "zero-cache Config", "searchTitle": "Log Slow Hydrate Threshold", "sectionTitle": "Log Slow Hydrate Threshold", @@ -8494,7 +8602,7 @@ "kind": "section" }, { - "id": "619-zero-cache-config#log-slow-row-threshold", + "id": "627-zero-cache-config#log-slow-row-threshold", "title": "zero-cache Config", "searchTitle": "Log Slow Row Threshold", "sectionTitle": "Log Slow Row Threshold", @@ -8504,7 +8612,7 @@ "kind": "section" }, { - "id": "620-zero-cache-config#mutate-api-key", + "id": "628-zero-cache-config#mutate-api-key", "title": "zero-cache Config", "searchTitle": "Mutate API Key", "sectionTitle": "Mutate API Key", @@ -8514,7 +8622,7 @@ "kind": "section" }, { - "id": "621-zero-cache-config#mutate-allowed-client-headers", + "id": "629-zero-cache-config#mutate-allowed-client-headers", "title": "zero-cache Config", "searchTitle": "Mutate Allowed Client Headers", "sectionTitle": "Mutate Allowed Client Headers", @@ -8524,7 +8632,7 @@ "kind": "section" }, { - "id": "622-zero-cache-config#mutate-allowed-request-headers", + "id": "630-zero-cache-config#mutate-allowed-request-headers", "title": "zero-cache Config", "searchTitle": "Mutate Allowed Request Headers", "sectionTitle": "Mutate Allowed Request Headers", @@ -8534,7 +8642,7 @@ "kind": "section" }, { - "id": "623-zero-cache-config#mutate-forward-cookies", + "id": "631-zero-cache-config#mutate-forward-cookies", "title": "zero-cache Config", "searchTitle": "Mutate Forward Cookies", "sectionTitle": "Mutate Forward Cookies", @@ -8544,7 +8652,7 @@ "kind": "section" }, { - "id": "624-zero-cache-config#mutate-url", + "id": "632-zero-cache-config#mutate-url", "title": "zero-cache Config", "searchTitle": "Mutate URL", "sectionTitle": "Mutate URL", @@ -8554,7 +8662,7 @@ "kind": "section" }, { - "id": "625-zero-cache-config#number-of-sync-workers", + "id": "633-zero-cache-config#number-of-sync-workers", "title": "zero-cache Config", "searchTitle": "Number of Sync Workers", "sectionTitle": "Number of Sync Workers", @@ -8564,7 +8672,7 @@ "kind": "section" }, { - "id": "626-zero-cache-config#per-user-mutation-limit-max", + "id": "634-zero-cache-config#per-user-mutation-limit-max", "title": "zero-cache Config", "searchTitle": "Per User Mutation Limit Max", "sectionTitle": "Per User Mutation Limit Max", @@ -8574,7 +8682,7 @@ "kind": "section" }, { - "id": "627-zero-cache-config#per-user-mutation-limit-window-ms", + "id": "635-zero-cache-config#per-user-mutation-limit-window-ms", "title": "zero-cache Config", "searchTitle": "Per User Mutation Limit Window (ms)", "sectionTitle": "Per User Mutation Limit Window (ms)", @@ -8584,17 +8692,17 @@ "kind": "section" }, { - "id": "628-zero-cache-config#pg-replication-slot-failover", + "id": "636-zero-cache-config#pg-replication-slot-failover", "title": "zero-cache Config", "searchTitle": "PG Replication Slot Failover", "sectionTitle": "PG Replication Slot Failover", "sectionId": "pg-replication-slot-failover", "url": "/docs/zero-cache-config", - "content": "For upstream Postgres 17+, creates replication slots with the failover flag enabled so they can be synchronized to a standby and survive a failover. This requires additional Postgres-side configuration on your provider; see High Availability and Failover. Has no effect on Postgres versions before 17. flag: --upstream-pg-replication-slot-failover env: ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER default: false", + "content": "For upstream Postgres 17+, creates replication slots with the failover flag enabled so they can be synchronized to a standby and survive a failover. This requires additional Postgres-side configuration on your provider; see High Availability. Has no effect on Postgres versions before 17. flag: --upstream-pg-replication-slot-failover env: ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER default: false", "kind": "section" }, { - "id": "629-zero-cache-config#port", + "id": "637-zero-cache-config#port", "title": "zero-cache Config", "searchTitle": "Port", "sectionTitle": "Port", @@ -8604,7 +8712,7 @@ "kind": "section" }, { - "id": "630-zero-cache-config#query-api-key", + "id": "638-zero-cache-config#query-api-key", "title": "zero-cache Config", "searchTitle": "Query API Key", "sectionTitle": "Query API Key", @@ -8614,7 +8722,7 @@ "kind": "section" }, { - "id": "631-zero-cache-config#query-allowed-client-headers", + "id": "639-zero-cache-config#query-allowed-client-headers", "title": "zero-cache Config", "searchTitle": "Query Allowed Client Headers", "sectionTitle": "Query Allowed Client Headers", @@ -8624,7 +8732,7 @@ "kind": "section" }, { - "id": "632-zero-cache-config#query-allowed-request-headers", + "id": "640-zero-cache-config#query-allowed-request-headers", "title": "zero-cache Config", "searchTitle": "Query Allowed Request Headers", "sectionTitle": "Query Allowed Request Headers", @@ -8634,7 +8742,7 @@ "kind": "section" }, { - "id": "633-zero-cache-config#query-forward-cookies", + "id": "641-zero-cache-config#query-forward-cookies", "title": "zero-cache Config", "searchTitle": "Query Forward Cookies", "sectionTitle": "Query Forward Cookies", @@ -8644,7 +8752,7 @@ "kind": "section" }, { - "id": "634-zero-cache-config#query-hydration-stats", + "id": "642-zero-cache-config#query-hydration-stats", "title": "zero-cache Config", "searchTitle": "Query Hydration Stats", "sectionTitle": "Query Hydration Stats", @@ -8654,7 +8762,7 @@ "kind": "section" }, { - "id": "635-zero-cache-config#query-url", + "id": "643-zero-cache-config#query-url", "title": "zero-cache Config", "searchTitle": "Query URL", "sectionTitle": "Query URL", @@ -8664,7 +8772,7 @@ "kind": "section" }, { - "id": "636-zero-cache-config#replica-file", + "id": "644-zero-cache-config#replica-file", "title": "zero-cache Config", "searchTitle": "Replica File", "sectionTitle": "Replica File", @@ -8674,7 +8782,7 @@ "kind": "section" }, { - "id": "637-zero-cache-config#replica-vacuum-interval-hours", + "id": "645-zero-cache-config#replica-vacuum-interval-hours", "title": "zero-cache Config", "searchTitle": "Replica Vacuum Interval Hours", "sectionTitle": "Replica Vacuum Interval Hours", @@ -8684,17 +8792,17 @@ "kind": "section" }, { - "id": "638-zero-cache-config#replication-lag-report-interval-ms", + "id": "646-zero-cache-config#replication-lag-report-interval-ms", "title": "zero-cache Config", "searchTitle": "Replication Lag Report Interval (ms)", "sectionTitle": "Replication Lag Report Interval (ms)", "sectionId": "replication-lag-report-interval-ms", "url": "/docs/zero-cache-config", - "content": "The minimum interval at which replication lag reports are written upstream and reported via the zero.replication.total_lag OpenTelemetry metric. Because replication lag reports are only issued after the previous one was received, the actual interval between reports may be longer when there is a backlog in the replication stream. This feature requires write access to upstream Postgres (uses pg_logical_emit_message()). For PostgreSQL 17+, lag measurements accurately reflect committed write latency (single-digit milliseconds). For PostgreSQL 16 and earlier, measurements may appear 50-100ms longer due to flush behavior. A negative or 0 value disables lag reporting. Even if otel is not enabled, info and warn-level logs are emitted for large lag values. flag: --replication-lag-report-interval-ms env: ZERO_REPLICATION_LAG_REPORT_INTERVAL_MS default: 30_000", + "content": "The minimum interval at which replication lag reports are written upstream and reported via the zero.replication.total_lag OpenTelemetry metric. If an expected report is not received before the next interval, Zero emits a new report and increments zero.replication.lag_report_retries. This feature requires write access to upstream Postgres (uses pg_logical_emit_message()). For PostgreSQL 17+, lag measurements accurately reflect committed write latency (single-digit milliseconds). For PostgreSQL 16 and earlier, measurements may appear 50-100ms longer due to flush behavior. A negative or 0 value disables lag reporting. Even if otel is not enabled, info and warn-level logs are emitted for large lag values. flag: --replication-lag-report-interval-ms env: ZERO_REPLICATION_LAG_REPORT_INTERVAL_MS default: 30_000", "kind": "section" }, { - "id": "639-zero-cache-config#server-version", + "id": "647-zero-cache-config#server-version", "title": "zero-cache Config", "searchTitle": "Server Version", "sectionTitle": "Server Version", @@ -8704,7 +8812,7 @@ "kind": "section" }, { - "id": "640-zero-cache-config#shadow-sync-enabled", + "id": "648-zero-cache-config#shadow-sync-enabled", "title": "zero-cache Config", "searchTitle": "Shadow Sync Enabled", "sectionTitle": "Shadow Sync Enabled", @@ -8714,7 +8822,7 @@ "kind": "section" }, { - "id": "641-zero-cache-config#shadow-sync-interval-hours", + "id": "649-zero-cache-config#shadow-sync-interval-hours", "title": "zero-cache Config", "searchTitle": "Shadow Sync Interval Hours", "sectionTitle": "Shadow Sync Interval Hours", @@ -8724,7 +8832,7 @@ "kind": "section" }, { - "id": "642-zero-cache-config#shadow-sync-sample-rate", + "id": "650-zero-cache-config#shadow-sync-sample-rate", "title": "zero-cache Config", "searchTitle": "Shadow Sync Sample Rate", "sectionTitle": "Shadow Sync Sample Rate", @@ -8734,7 +8842,7 @@ "kind": "section" }, { - "id": "643-zero-cache-config#shadow-sync-max-rows-per-table", + "id": "651-zero-cache-config#shadow-sync-max-rows-per-table", "title": "zero-cache Config", "searchTitle": "Shadow Sync Max Rows Per Table", "sectionTitle": "Shadow Sync Max Rows Per Table", @@ -8744,7 +8852,7 @@ "kind": "section" }, { - "id": "644-zero-cache-config#storage-db-temp-dir", + "id": "652-zero-cache-config#storage-db-temp-dir", "title": "zero-cache Config", "searchTitle": "Storage DB Temp Dir", "sectionTitle": "Storage DB Temp Dir", @@ -8754,7 +8862,7 @@ "kind": "section" }, { - "id": "645-zero-cache-config#task-id", + "id": "653-zero-cache-config#task-id", "title": "zero-cache Config", "searchTitle": "Task ID", "sectionTitle": "Task ID", @@ -8764,7 +8872,7 @@ "kind": "section" }, { - "id": "646-zero-cache-config#upstream-max-connections", + "id": "654-zero-cache-config#upstream-max-connections", "title": "zero-cache Config", "searchTitle": "Upstream Max Connections", "sectionTitle": "Upstream Max Connections", @@ -8774,7 +8882,7 @@ "kind": "section" }, { - "id": "647-zero-cache-config#upstream-pg-replication-slot-failover", + "id": "655-zero-cache-config#upstream-pg-replication-slot-failover", "title": "zero-cache Config", "searchTitle": "Upstream PG Replication Slot Failover", "sectionTitle": "Upstream PG Replication Slot Failover", @@ -8784,7 +8892,7 @@ "kind": "section" }, { - "id": "648-zero-cache-config#websocket-compression", + "id": "656-zero-cache-config#websocket-compression", "title": "zero-cache Config", "searchTitle": "Websocket Compression", "sectionTitle": "Websocket Compression", @@ -8794,7 +8902,7 @@ "kind": "section" }, { - "id": "649-zero-cache-config#websocket-compression-options", + "id": "657-zero-cache-config#websocket-compression-options", "title": "zero-cache Config", "searchTitle": "Websocket Compression Options", "sectionTitle": "Websocket Compression Options", @@ -8804,7 +8912,7 @@ "kind": "section" }, { - "id": "650-zero-cache-config#websocket-max-payload-bytes", + "id": "658-zero-cache-config#websocket-max-payload-bytes", "title": "zero-cache Config", "searchTitle": "Websocket Max Payload Bytes", "sectionTitle": "Websocket Max Payload Bytes", @@ -8814,7 +8922,7 @@ "kind": "section" }, { - "id": "651-zero-cache-config#yield-threshold-ms", + "id": "659-zero-cache-config#yield-threshold-ms", "title": "zero-cache Config", "searchTitle": "Yield Threshold (ms)", "sectionTitle": "Yield Threshold (ms)", @@ -8824,7 +8932,7 @@ "kind": "section" }, { - "id": "652-zero-cache-config#deprecated-flags", + "id": "660-zero-cache-config#deprecated-flags", "title": "zero-cache Config", "searchTitle": "Deprecated Flags", "sectionTitle": "Deprecated Flags", @@ -8834,7 +8942,7 @@ "kind": "section" }, { - "id": "653-zero-cache-config#auth-jwk", + "id": "661-zero-cache-config#auth-jwk", "title": "zero-cache Config", "searchTitle": "Auth JWK", "sectionTitle": "Auth JWK", @@ -8844,7 +8952,7 @@ "kind": "section" }, { - "id": "654-zero-cache-config#auth-jwks-url", + "id": "662-zero-cache-config#auth-jwks-url", "title": "zero-cache Config", "searchTitle": "Auth JWKS URL", "sectionTitle": "Auth JWKS URL", @@ -8854,7 +8962,7 @@ "kind": "section" }, { - "id": "655-zero-cache-config#auth-secret", + "id": "663-zero-cache-config#auth-secret", "title": "zero-cache Config", "searchTitle": "Auth Secret", "sectionTitle": "Auth Secret", @@ -8864,7 +8972,7 @@ "kind": "section" }, { - "id": "75-zql", + "id": "76-zql", "title": "ZQL", "searchTitle": "ZQL", "url": "/docs/zql", @@ -8974,7 +9082,7 @@ "kind": "page" }, { - "id": "656-zql#create-a-builder", + "id": "664-zql#create-a-builder", "title": "ZQL", "searchTitle": "Create a Builder", "sectionTitle": "Create a Builder", @@ -8984,7 +9092,7 @@ "kind": "section" }, { - "id": "657-zql#select", + "id": "665-zql#select", "title": "ZQL", "searchTitle": "Select", "sectionTitle": "Select", @@ -8994,7 +9102,7 @@ "kind": "section" }, { - "id": "658-zql#ordering", + "id": "666-zql#ordering", "title": "ZQL", "searchTitle": "Ordering", "sectionTitle": "Ordering", @@ -9004,7 +9112,7 @@ "kind": "section" }, { - "id": "659-zql#limit", + "id": "667-zql#limit", "title": "ZQL", "searchTitle": "Limit", "sectionTitle": "Limit", @@ -9014,7 +9122,7 @@ "kind": "section" }, { - "id": "660-zql#paging", + "id": "668-zql#paging", "title": "ZQL", "searchTitle": "Paging", "sectionTitle": "Paging", @@ -9024,7 +9132,7 @@ "kind": "section" }, { - "id": "661-zql#getting-a-single-result", + "id": "669-zql#getting-a-single-result", "title": "ZQL", "searchTitle": "Getting a Single Result", "sectionTitle": "Getting a Single Result", @@ -9034,7 +9142,7 @@ "kind": "section" }, { - "id": "662-zql#relationships", + "id": "670-zql#relationships", "title": "ZQL", "searchTitle": "Relationships", "sectionTitle": "Relationships", @@ -9044,7 +9152,7 @@ "kind": "section" }, { - "id": "663-zql#refining-relationships", + "id": "671-zql#refining-relationships", "title": "ZQL", "searchTitle": "Refining Relationships", "sectionTitle": "Refining Relationships", @@ -9054,7 +9162,7 @@ "kind": "section" }, { - "id": "664-zql#nested-relationships", + "id": "672-zql#nested-relationships", "title": "ZQL", "searchTitle": "Nested Relationships", "sectionTitle": "Nested Relationships", @@ -9064,7 +9172,7 @@ "kind": "section" }, { - "id": "665-zql#where", + "id": "673-zql#where", "title": "ZQL", "searchTitle": "Where", "sectionTitle": "Where", @@ -9074,7 +9182,7 @@ "kind": "section" }, { - "id": "666-zql#comparison-operators", + "id": "674-zql#comparison-operators", "title": "ZQL", "searchTitle": "Comparison Operators", "sectionTitle": "Comparison Operators", @@ -9084,7 +9192,7 @@ "kind": "section" }, { - "id": "667-zql#equals-is-the-default-comparison-operator", + "id": "675-zql#equals-is-the-default-comparison-operator", "title": "ZQL", "searchTitle": "Equals is the Default Comparison Operator", "sectionTitle": "Equals is the Default Comparison Operator", @@ -9094,7 +9202,7 @@ "kind": "section" }, { - "id": "668-zql#comparing-to-null", + "id": "676-zql#comparing-to-null", "title": "ZQL", "searchTitle": "Comparing to null", "sectionTitle": "Comparing to null", @@ -9104,7 +9212,7 @@ "kind": "section" }, { - "id": "669-zql#comparing-to-undefined", + "id": "677-zql#comparing-to-undefined", "title": "ZQL", "searchTitle": "Comparing to undefined", "sectionTitle": "Comparing to undefined", @@ -9114,7 +9222,7 @@ "kind": "section" }, { - "id": "670-zql#compound-filters", + "id": "678-zql#compound-filters", "title": "ZQL", "searchTitle": "Compound Filters", "sectionTitle": "Compound Filters", @@ -9124,7 +9232,7 @@ "kind": "section" }, { - "id": "671-zql#comparing-literal-values", + "id": "679-zql#comparing-literal-values", "title": "ZQL", "searchTitle": "Comparing Literal Values", "sectionTitle": "Comparing Literal Values", @@ -9134,7 +9242,7 @@ "kind": "section" }, { - "id": "672-zql#relationship-filters", + "id": "680-zql#relationship-filters", "title": "ZQL", "searchTitle": "Relationship Filters", "sectionTitle": "Relationship Filters", @@ -9144,7 +9252,7 @@ "kind": "section" }, { - "id": "673-zql#type-helpers", + "id": "681-zql#type-helpers", "title": "ZQL", "searchTitle": "Type Helpers", "sectionTitle": "Type Helpers", @@ -9154,7 +9262,7 @@ "kind": "section" }, { - "id": "674-zql#planning", + "id": "682-zql#planning", "title": "ZQL", "searchTitle": "Planning", "sectionTitle": "Planning", @@ -9164,7 +9272,7 @@ "kind": "section" }, { - "id": "675-zql#inspecting-query-plans", + "id": "683-zql#inspecting-query-plans", "title": "ZQL", "searchTitle": "Inspecting Query Plans", "sectionTitle": "Inspecting Query Plans", @@ -9174,7 +9282,7 @@ "kind": "section" }, { - "id": "676-zql#manually-flipping-joins", + "id": "684-zql#manually-flipping-joins", "title": "ZQL", "searchTitle": "Manually Flipping Joins", "sectionTitle": "Manually Flipping Joins", @@ -9184,7 +9292,7 @@ "kind": "section" }, { - "id": "677-zql#scalar-subqueries", + "id": "685-zql#scalar-subqueries", "title": "ZQL", "searchTitle": "Scalar Subqueries", "sectionTitle": "Scalar Subqueries", @@ -9194,7 +9302,7 @@ "kind": "section" }, { - "id": "678-zql#why-it-matters", + "id": "686-zql#why-it-matters", "title": "ZQL", "searchTitle": "Why It Matters", "sectionTitle": "Why It Matters", @@ -9204,7 +9312,7 @@ "kind": "section" }, { - "id": "679-zql#trade-offs", + "id": "687-zql#trade-offs", "title": "ZQL", "searchTitle": "Trade-offs", "sectionTitle": "Trade-offs", @@ -9214,7 +9322,7 @@ "kind": "section" }, { - "id": "680-zql#future-work", + "id": "688-zql#future-work", "title": "ZQL", "searchTitle": "Future Work", "sectionTitle": "Future Work", diff --git a/contents/docs/connecting-to-postgres.mdx b/contents/docs/connecting-to-postgres.mdx index 0f86aadb..39e79f38 100644 --- a/contents/docs/connecting-to-postgres.mdx +++ b/contents/docs/connecting-to-postgres.mdx @@ -57,6 +57,18 @@ After your server restarts, show the `wal_level` again to ensure it has changed: psql -c 'SHOW wal_level' ``` +### Socket Inactivity Timeout + +`zero-cache` monitors wire activity on its Postgres connections so it can recover when a proxy or network failure leaves a half-open socket. The watchdog samples each connection every 120,000 milliseconds by default and resets it after one to two intervals without any bytes read or written. In-flight queries on a reset connection are rejected and can recover through their normal retry or restart paths. + +Wire activity resets the watchdog, so streaming operations such as `COPY` remain active. A statement that legitimately computes without sending any data for several minutes can be interrupted. Set `ZERO_PG_SOCKET_INACTIVITY_TIMEOUT` to a longer sampling interval in milliseconds when running such statements: + +```bash +ZERO_PG_SOCKET_INACTIVITY_TIMEOUT=600000 +``` + +Set the value to `0` to disable the watchdog. + ### Bounding WAL Size For development databases, you can set a `max_slot_wal_keep_size` value in Postgres. This will help limit the amount of WAL kept around. diff --git a/contents/docs/otel.mdx b/contents/docs/otel.mdx index 485c9298..ca49af39 100644 --- a/contents/docs/otel.mdx +++ b/contents/docs/otel.mdx @@ -152,78 +152,84 @@ This callback is called before sending WebSocket messages that trigger API serve ### zero.replication -| Metric | Type | Unit | Description | -| ------------------------------------ | --------- | ----- | ------------------------------------------------------------------------------------------------------------------- | -| `upstream_lag` | Gauge | ms | Latency from sending a replication report to receiving it in the stream | -| `replica_lag` | Gauge | ms | Latency from receiving a replication report to it reaching the replica | -| `total_lag` | Gauge | ms | End-to-end replication latency. Grows as an estimate if the next report hasn't arrived | -| `last_total_lag` | Gauge | ms | End-to-end latency of the most recently received report. Unlike `total_lag`, does not grow if reports stop arriving | -| `events` | Counter | | Number of replication events processed | -| `transactions` | Counter | | Count of replicated transactions | -| `changes` | Counter | | Count of replicated changes, including DML and DDL statements | -| `slot_health` | Gauge | 1 | One-hot status for the active logical replication slot: `ok`, `unreserved`, `lost`, `missing`, or `unknown` | -| `slot_retained_wal_bytes` | Gauge | bytes | WAL bytes retained by the active logical replication slot | -| `slot_safe_wal_bytes` | Gauge | bytes | Remaining WAL capacity before the active logical replication slot is lost; omitted when Postgres reports no value | -| `initial_sync_runs` | Counter | | Number of initial-sync runs | -| `initial_sync_duration` | Histogram | s | Wall-clock duration of an initial-sync run | -| `initial_sync_copy_duration` | Histogram | s | Wall-clock duration of the COPY phase for a successful initial-sync run | -| `initial_sync_copy_other_duration` | Histogram | s | Initial-sync duration excluding SQLite flush and index time for a successful run | -| `initial_sync_flush_duration` | Histogram | s | Total SQLite flush time for a successful initial-sync run | -| `initial_sync_index_duration` | Histogram | s | SQLite index creation time for a successful initial-sync run | -| `initial_sync_rows` | Counter | | Rows copied during successful initial-sync runs | -| `initial_sync_copy_stream` | Counter | bytes | PostgreSQL COPY stream bytes processed during initial sync, including in-progress and failed runs | -| `initial_sync_completed_copy_stream` | Counter | bytes | PostgreSQL COPY stream bytes processed during successful initial-sync runs | -| `initial_sync_copy_chunks` | Counter | | PostgreSQL COPY stream chunks processed during initial sync | -| `shadow-sync-runs` | Counter | | Number of [shadow initial-sync](/docs/zero-cache-config#shadow-sync-enabled) runs, labeled by `result` | -| `shadow-sync-duration` | Histogram | s | Wall-clock duration of a shadow initial-sync run, labeled by `result` | -| `flow_control.active_subscribers` | Gauge | | Active change-stream subscribers receiving live changes | -| `flow_control.queued_subscribers` | Gauge | | Change-stream subscribers waiting for the current transaction to finish before activation | -| `flow_control.pending_messages` | Gauge | | Downstream change-stream messages not yet acknowledged by subscribers | -| `flow_control.backlog_messages` | Gauge | | Live change-stream messages buffered while subscribers catch up | -| `flow_control.backlog_bytes` | Gauge | bytes | Live change-stream bytes buffered while subscribers catch up | -| `flow_control.max_backlog_bytes` | Gauge | bytes | Maximum live change-stream bytes buffered by a single subscriber | -| `flow_control.waits` | Counter | | Completed flow-control checkpoints | -| `flow_control.wait_duration` | Histogram | s | Time replication waits at flow-control checkpoints | +| Metric | Type | Unit | Description | +| ------------------------------------ | --------- | ----- | -------------------------------------------------------------------------------------------------------------------- | +| `upstream_lag` | Gauge | ms | Latency from sending a replication report to receiving it in the stream | +| `replica_lag` | Gauge | ms | Latency from receiving a replication report to it reaching the replica | +| `total_lag` | Gauge | ms | Measured end-to-end latency of the most recently received replication report; does not grow if reports stop arriving | +| `last_total_lag` | Gauge | ms | Alias of `total_lag`, retained for dashboards that explicitly use the non-extrapolated metric | +| `lag_report_retries` | Counter | | Replication lag reports retried because an expected report did not arrive before the next report interval | +| `events` | Counter | | Number of replication events processed | +| `transactions` | Counter | | Count of replicated transactions | +| `changes` | Counter | | Count of replicated changes, including DML and DDL statements | +| `slot_health` | Gauge | 1 | One-hot status for the active logical replication slot: `ok`, `unreserved`, `lost`, `missing`, or `unknown` | +| `slot_retained_wal_bytes` | Gauge | bytes | WAL bytes retained by the active logical replication slot | +| `slot_safe_wal_bytes` | Gauge | bytes | Remaining WAL capacity before the active logical replication slot is lost; omitted when Postgres reports no value | +| `initial_sync_runs` | Counter | | Number of initial-sync runs | +| `initial_sync_duration` | Histogram | s | Wall-clock duration of an initial-sync run | +| `initial_sync_copy_duration` | Histogram | s | Wall-clock duration of the COPY phase for a successful initial-sync run | +| `initial_sync_copy_other_duration` | Histogram | s | Initial-sync duration excluding SQLite flush and index time for a successful run | +| `initial_sync_flush_duration` | Histogram | s | Total SQLite flush time for a successful initial-sync run | +| `initial_sync_index_duration` | Histogram | s | SQLite index creation time for a successful initial-sync run | +| `initial_sync_rows` | Counter | | Rows copied during successful initial-sync runs | +| `initial_sync_copy_stream` | Counter | bytes | PostgreSQL COPY stream bytes processed during initial sync, including in-progress and failed runs | +| `initial_sync_completed_copy_stream` | Counter | bytes | PostgreSQL COPY stream bytes processed during successful initial-sync runs | +| `initial_sync_copy_chunks` | Counter | | PostgreSQL COPY stream chunks processed during initial sync | +| `shadow-sync-runs` | Counter | | Number of [shadow initial-sync](/docs/zero-cache-config#shadow-sync-enabled) runs, labeled by `result` | +| `shadow-sync-duration` | Histogram | s | Wall-clock duration of a shadow initial-sync run, labeled by `result` | +| `flow_control.active_subscribers` | Gauge | | Active change-stream subscribers receiving live changes | +| `flow_control.queued_subscribers` | Gauge | | Change-stream subscribers waiting for the current transaction to finish before activation | +| `flow_control.pending_messages` | Gauge | | Downstream change-stream messages not yet acknowledged by subscribers | +| `flow_control.backlog_messages` | Gauge | | Live change-stream messages buffered while subscribers catch up | +| `flow_control.backlog_bytes` | Gauge | bytes | Live change-stream bytes buffered while subscribers catch up | +| `flow_control.max_backlog_bytes` | Gauge | bytes | Maximum live change-stream bytes buffered by a single subscriber | +| `flow_control.waits` | Counter | | Completed flow-control checkpoints | +| `flow_control.wait_duration` | Histogram | s | Time replication waits at flow-control checkpoints | + +`total_lag` and `last_total_lag` now report the same latest measured round trip and do not grow when reports stop arriving. Use `lag_report_retries` to detect a stalled or missing report stream. ### zero.sync -| Metric | Type | Unit | Description | -| ----------------------------------- | ------------- | ---- | -------------------------------------------------------------------------------------------------------------- | -| `max-protocol-version` | Gauge | | Highest sync protocol version seen from connecting clients | -| `active-clients` | UpDownCounter | | Number of currently connected sync clients | -| `active-client-groups` | Gauge | | Number of active ViewSyncerService instances in a syncer worker | -| `queries` | Gauge | | Active IVM pipelines across all client groups in a syncer worker | -| `rows` | Gauge | | CVR-tracked rows across all client groups in a syncer worker | -| `serving_lag` | Gauge | ms | Longest time locally ready replica changes have remained unserved across active ViewSyncer client groups | -| `serving_lag_stats` | Gauge | ms | Distribution of serving lag across active ViewSyncer client groups | -| `serving_lagging_client_groups` | Gauge | | Active ViewSyncer client groups with locally ready replica changes not yet served to clients | -| `view_syncer_lag` | Histogram | s | Time from replica changes becoming ready to ViewSyncer output, sampled once per minute per active client group | -| `view_syncer_hydration` | Histogram | s | Time from a ViewSyncer query sync requiring hydration until output, per client group | -| `lock-wait-time` | Histogram | s | Time spent waiting to acquire the ViewSyncerService lock per operation | -| `pipeline-resets` | Counter | | Count of pipeline resets, labeled by `reason` | -| `hydration` | Counter | | Number of query hydrations | -| `hydration-time` | Histogram | s | Time to hydrate a query | -| `advance-time` | Histogram | s | Time to advance all queries for a client group after applying a transaction | -| `poke.time` | Histogram | s | Time per poke transaction (excludes canceled/noop pokes) | -| `poke.transactions` | Counter | | Count of poke transactions | -| `poke.rows` | Counter | | Count of poked rows | -| `cvr.load_attempts` | Counter | | CVR load attempts | -| `cvr.load_duration` | Histogram | s | Time to load a CVR | -| `cvr.flush_attempts` | Counter | | CVR flush attempts | -| `cvr.flush-time` | Histogram | s | Time to flush a CVR transaction | -| `cvr.rows-flushed` | Counter | | Number of changed rows flushed to a CVR | -| `websocket.open_connections` | UpDownCounter | | Open client WebSocket connections | -| `websocket.connection_attempts` | Counter | | Client WebSocket connection attempts | -| `websocket.connection_successes` | Counter | | Client WebSocket connections successfully initialized | -| `websocket.connection_failures` | Counter | | Client WebSocket connection attempts that failed before initialization | -| `websocket.errors` | Counter | | Client WebSocket error events | -| `ivm.advance-time` | Histogram | s | Time to advance IVM queries in response to a single change | -| `ivm.conflict-rows-deleted` | Counter | | Rows deleted because they conflicted with an added row | -| `query.transformations` | Counter | | Number of query transformations performed | -| `query.transformation-time` | Histogram | s | Time to transform custom queries via API server | -| `query.transformation-hash-changes` | Counter | | Times a query transformation hash changed | -| `query.transformation-no-ops` | Counter | | Times a query transformation was a no-op | -| `query.row-set-signature-drifts` | Counter | | Unchanged query rehydrations whose row-set signature differs from the CVR, forcing a config-version bump | +| Metric | Type | Unit | Description | +| ------------------------------------------ | ------------- | ---- | --------------------------------------------------------------------------------------------------------- | +| `max-protocol-version` | Gauge | | Highest sync protocol version seen from connecting clients | +| `active-clients` | UpDownCounter | | Number of currently connected sync clients | +| `active-client-groups` | Gauge | | Number of active ViewSyncerService instances in a syncer worker | +| `queries` | Gauge | | Active IVM pipelines across all client groups in a syncer worker | +| `rows` | Gauge | | CVR-tracked rows across all client groups in a syncer worker | +| `serving_lag` | Gauge | ms | Longest time locally ready replica changes have remained unserved across eligible active client groups | +| `serving_lag_stats` | Gauge | ms | Distribution of serving lag across eligible active client groups | +| `serving_lagging_client_groups` | Gauge | | Eligible active client groups with locally ready replica changes not yet served to clients | +| `view_syncer_lag` | Histogram | s | Time from replica changes becoming ready to ViewSyncer output, sampled once per minute per eligible group | +| `view_syncer_hydration` | Histogram | s | Time from a ViewSyncer query sync requiring hydration until output, per client group | +| `lock-wait-time` | Histogram | s | Time spent waiting to acquire the ViewSyncerService lock per operation | +| `pipeline-resets` | Counter | | Count of pipeline resets, labeled by `reason` | +| `hydration` | Counter | | Number of query hydrations | +| `hydration-time` | Histogram | s | Time to hydrate a query | +| `advance-time` | Histogram | s | Time to advance all queries for a client group after applying a transaction | +| `poke.time` | Histogram | s | Time per poke transaction (excludes canceled/noop pokes) | +| `poke.transactions` | Counter | | Count of poke transactions | +| `poke.rows` | Counter | | Count of poked rows | +| `cvr.load_attempts` | Counter | | CVR load attempts | +| `cvr.load_duration` | Histogram | s | Time to load a CVR | +| `cvr.flush_attempts` | Counter | | CVR flush attempts | +| `cvr.flush-time` | Histogram | s | Time to flush a CVR transaction | +| `cvr.rows-flushed` | Counter | | Number of changed rows flushed to a CVR | +| `websocket.open_connections` | UpDownCounter | | Open client WebSocket connections | +| `websocket.connection_attempts` | Counter | | Client WebSocket connection attempts | +| `websocket.connection_successes` | Counter | | Client WebSocket connections successfully initialized | +| `websocket.connection_failures` | Counter | | Client WebSocket connection attempts that failed before initialization | +| `websocket.errors` | Counter | | Client WebSocket error events | +| `ivm.advance-time` | Histogram | s | Time to advance IVM queries in response to a single change | +| `ivm.conflict-rows-deleted` | Counter | | Rows deleted because they conflicted with an added row | +| `query.transformations` | Counter | | Number of query transformations performed | +| `query.transformation-time` | Histogram | s | Time to transform custom queries via API server | +| `query.transformation-hash-changes` | Counter | | Times a query transformation hash changed | +| `query.transformation-no-ops` | Counter | | Times a query transformation was a no-op | +| `query.row-set-signature-drifts` | Counter | | Unchanged query rehydrations whose row-set signature differs from the CVR, forcing a config-version bump | +| `query.same-hash-rehydrations-forced-bump` | Counter | | Same-hash query rehydrations that force a config-version bump so changed rows are delivered | + +Serving-lag metrics include only client groups with at least one connected client and a validated background connection context. Retained groups without an eligible connection do not contribute lag. ### zero.mutation diff --git a/contents/docs/release-notes/1.9.mdx b/contents/docs/release-notes/1.9.mdx new file mode 100644 index 00000000..63604a56 --- /dev/null +++ b/contents/docs/release-notes/1.9.mdx @@ -0,0 +1,44 @@ +--- +title: Zero 1.9 +description: Query Correctness and Reliability +--- + +## Installation + +```bash +npm install @rocicorp/zero@1.9 +``` + +You can use `zero-cache` from Docker Hub or GHCR: + +```bash +docker pull rocicorp/zero:1.9.0 +# or +docker pull ghcr.io/rocicorp/zero:1.9.0 +``` + +## Overview + +Zero 1.9 improves query correctness and `zero-cache` reliability. + +## Performance + +Deferred. + +## Fixes + +- [Ordered queries now paginate and maintain windows correctly when cursor fields contain `NULL`, including compound tie-break fields and reverse walks.](https://github.com/rocicorp/mono/pull/6121) This prevents skipped rows, empty windows, and related `Bound should be set` failures. (thanks [@YevheniiKotyrlo](https://github.com/YevheniiKotyrlo)!) +- [Schema construction, CRUD mutators, and materialized views now preserve a key named `__proto__` as user data instead of invoking JavaScript's legacy prototype setter.](https://github.com/rocicorp/mono/pull/6185) (thanks [@tjenkinson](https://github.com/tjenkinson)!) +- [Clients now receive changed rows after a server-side query is rebuilt, instead of retaining stale results in a rare rehydration case.](https://github.com/rocicorp/mono/pull/6196) +- [`zero-cache` now bounds its SQLite prepared-statement caches with LRU eviction, preventing unbounded statement retention when applications generate many distinct query shapes.](https://github.com/rocicorp/mono/pull/6202) +- [Replication lag reports now retry when an expected report is missing](https://github.com/rocicorp/mono/pull/6187), and [serving-lag metrics exclude disconnected or not-yet-validated client groups](https://github.com/rocicorp/mono/pull/6219). See the updated [OpenTelemetry metric descriptions](/docs/otel#zeroreplication). +- [`zero-cache` now detects and resets PostgreSQL connections that stop carrying wire traffic](https://github.com/rocicorp/mono/pull/6220), [including over TLS](https://github.com/rocicorp/mono/pull/6221), allowing work to recover from proxy-created half-open sockets. See [Breaking Changes](#postgresql-socket-inactivity-timeout). +- [`zero-cache` now releases custom-query caches when client groups stop, preventing inactive groups from retaining timers and transformed queries.](https://github.com/rocicorp/mono/pull/6228) + +## Breaking Changes + +### PostgreSQL Socket Inactivity Timeout + +`zero-cache` now monitors wire activity on its PostgreSQL connections. By default, it checks every two minutes and resets a connection after one to two inactive intervals. This recovers half-open connections, but can interrupt a long-running statement that legitimately produces no network traffic. + +If legitimate Postgres operations can remain silent for this long, set [`ZERO_PG_SOCKET_INACTIVITY_TIMEOUT`](/docs/connecting-to-postgres#socket-inactivity-timeout) on `zero-cache` to a longer interval in milliseconds. Set it to `0` to disable the watchdog. diff --git a/contents/docs/release-notes/index.mdx b/contents/docs/release-notes/index.mdx index 9692efe0..b7da682f 100644 --- a/contents/docs/release-notes/index.mdx +++ b/contents/docs/release-notes/index.mdx @@ -2,6 +2,7 @@ title: Release Notes --- +- [Zero 1.9: Query Correctness and Reliability](/docs/release-notes/1.9) - [Zero 1.8: Observability and Reliability](/docs/release-notes/1.8) - [Zero 1.7: Query Correctness and Performance](/docs/release-notes/1.7) - [Zero 1.6: PlanetScale Failover Support](/docs/release-notes/1.6) diff --git a/contents/docs/zero-cache-config.mdx b/contents/docs/zero-cache-config.mdx index 2b0f6809..6df03098 100644 --- a/contents/docs/zero-cache-config.mdx +++ b/contents/docs/zero-cache-config.mdx @@ -545,7 +545,7 @@ default: `60000` ### PG Replication Slot Failover -For upstream Postgres 17+, creates replication slots with the `failover` flag enabled so they can be synchronized to a standby and survive a failover. This requires additional Postgres-side configuration on your provider; see [High Availability and Failover](/docs/connecting-to-postgres#high-availability-and-failover). Has no effect on Postgres versions before 17. +For upstream Postgres 17+, creates replication slots with the `failover` flag enabled so they can be synchronized to a standby and survive a failover. This requires additional Postgres-side configuration on your provider; see [High Availability](/docs/connecting-to-postgres#high-availability). Has no effect on Postgres versions before 17. flag: `--upstream-pg-replication-slot-failover`
env: `ZERO_UPSTREAM_PG_REPLICATION_SLOT_FAILOVER`
@@ -651,7 +651,7 @@ env: `ZERO_REPLICA_VACUUM_INTERVAL_HOURS`
### Replication Lag Report Interval (ms) -The minimum interval at which replication lag reports are written upstream and reported via the `zero.replication.total_lag` [OpenTelemetry metric](/docs/otel). Because replication lag reports are only issued after the previous one was received, the actual interval between reports may be longer when there is a backlog in the replication stream. +The minimum interval at which replication lag reports are written upstream and reported via the `zero.replication.total_lag` [OpenTelemetry metric](/docs/otel). If an expected report is not received before the next interval, Zero emits a new report and increments `zero.replication.lag_report_retries`. This feature requires write access to upstream Postgres (uses `pg_logical_emit_message()`). For PostgreSQL 17+, lag measurements accurately reflect committed write latency (single-digit milliseconds). For PostgreSQL 16 and earlier, measurements may appear 50-100ms longer due to flush behavior. A negative or 0 value disables lag reporting.