Zero 1.8
Observability and Reliability
Installation
npm install @rocicorp/zero@1.8You can now use zero-cache from GHCR:
docker pull rocicorp/zero:1.8.0
# or
docker pull ghcr.io/rocicorp/zero:1.8.0Overview
Zero 1.8 improves observability, performance, and reliability.
Features
- Request-header forwarding:
zero-cachecan forward selected WebSocket upgrade headers to custom APIs usingZERO_MUTATE_ALLOWED_REQUEST_HEADERSandZERO_QUERY_ALLOWED_REQUEST_HEADERS. (#6144, thanks @tjenkinson!) - GHCR Docker images: Zero images are now published to
ghcr.io/rocicorp/zeroas well as Docker Hub. (#6161) - Mutator result type:
MutatorResultis now exported from@rocicorp/zerofor typing helpers that await.clientor.server. (#6223) - Operational metrics:
zero-cacheadds 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 replicated one 50,000-change transaction at 17,456 changes/sec, up from 11,045 changes/sec in Zero 1.7. For fifty 1,000-change transactions, throughput rose from 10,760 to 16,766 changes/sec.
Replicating Large Transactions
Measured on M5 Pro. Higher is better.
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.
Maintaining orderBy() + limit() Queries
Measured on M5 Pro. Higher is better.
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.
Running Local ZQL Queries
Measured on M5 Pro. Lower is better.
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 handlenullstart-cursor fields. - Queries no longer appear
completewith stale or empty results after reconnect. - React Native reads now work with
op-sqlitev17. - 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.