Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion SOURCE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Source

Doc list and metadata from https://github.com/rocicorp/zero-docs
Upstream commit: b37d0de26315c8bc51989ada51021755be249a6c
Upstream commit: 6d3c69978b2b662f886bbc949916fcd037104384
Page bodies fetched from https://zero.rocicorp.dev/docs/{path} (build-rendered markdown)
3 changes: 3 additions & 0 deletions skills/zero-docs/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,7 @@
- [Zero 1.4](references/release-notes/1.4.md) — Performance and Reliability Improvements
- [Zero 1.5](references/release-notes/1.5.md) — Schema Change Improvements and Client Group Auth
- [Zero 1.6](references/release-notes/1.6.md) — PlanetScale Failover Support
- [Zero 1.7](references/release-notes/1.7.md) — Query Correctness and Performance
- [Zero 1.8](references/release-notes/1.8.md) — Observability and Reliability
- [Zero 1.9](references/release-notes/1.9.md) — Stability and Query Correctness
- [Release Notes](references/release-notes/index.md)
14 changes: 14 additions & 0 deletions skills/zero-docs/references/connecting-to-postgres.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ 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 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.

### WAL Sender Timeout

`zero-cache` uses Postgres's `wal_sender_timeout` setting to monitor its replication connection. When the timeout is greater than `0`, Zero sends keepalives and reconnects if the replication stream stops responding. The inbound timeout defaults to twice `wal_sender_timeout`.

A healthy WAL sender can sometimes remain silent longer than this while decoding WAL from unpublished tables or assembling a large transaction. Set [`ZERO_UPSTREAM_PG_STREAM_INBOUND_TIMEOUT_MS`](zero-cache-config.md#upstream-pg-stream-inbound-timeout) to widen Zero's inbound threshold without changing the server's timeout. Manual keepalive timing remains derived from `wal_sender_timeout`.

Setting `wal_sender_timeout` to `0` disables the timeout in Postgres and the related keepalive and reconnect checks in Zero, even when an inbound timeout override is configured. Other connection failure detection remains active.

### 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.
Expand Down
4 changes: 2 additions & 2 deletions skills/zero-docs/references/connection.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ Reads are allowed while `disconnected`, but writes are rejected and return an of

### Error

If `zero-cache` itself crashes, or if the [mutate](mutators.md) or [query](queries.md) endpoints return a network or HTTP error, Zero transitions to the `error` state.
If `zero-cache` crashes, or [mutate](mutators.md) or [query](queries.md) endpoints fail, Zero enters the `error` state. If the response code is `5xx`, `zero-cache` will retry up to four times.

This type of error is unlikely to resolve just by retrying, so Zero doesn't try. The app can retry the connection manually by calling `zero.connection.connect()`.
Zero does not retry from the `error` state. Call `zero.connection.connect()` to retry manually.

Reads are allowed while in the `error` state, but writes are rejected.

Expand Down
31 changes: 15 additions & 16 deletions skills/zero-docs/references/mutators.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ tx.mutate.user.insert({
})
```

If the Zero primary key already exists, `insert` will succeed without changing the row - use `upsert` to update an existing row.

Optional fields can be set to `null` to explicitly set the new field to `null`. They can also be set to `undefined` to take the default value (which is often `null` but can also be some generated value server-side):

```tsx
Expand Down Expand Up @@ -782,7 +784,7 @@ app.post('/api/zero/mutate', async c => {
})
```

If Zero receives any response from the mutate endpoint other than HTTP 200, 401, or 403, it will disconnect and enter the [error state](connection.md#error).
Responses other than 200, 401, or 403 enter the [error state](connection.md#error). `zero-cache` will retry on `5xx` up to four times before returning an error.

If Zero receives HTTP 401 or 403, the client will enter the needs auth state and require a manual reconnect. Use `zero.connection.connect()` for cookie auth or `zero.connection.connect({auth: newToken})` for token auth, then Zero will retry all queued mutations.

Expand Down Expand Up @@ -936,7 +938,7 @@ const read2 = await zero.run(
)
```

You can also wait for the server write to succeed:
You can also await `.server` for the server result:

```ts
const write = zero.mutate(
Expand All @@ -948,10 +950,9 @@ const write = zero.mutate(

const clientRes = await write.client
if (clientRes.type === 'error') {
throw new Error(
`Mutator failed on client`,
clientRes.error
)
throw new Error(`Mutator failed on client`, {
cause: clientRes.error
})
}

// optimistic write guaranteed to be present here, but not
Expand All @@ -960,25 +961,23 @@ const read1 = await zero.run(
queries.issue.byId('issue-123').one()
)

// Await server write – this involves a round-trip.
// Await the server result/acknowledgment. This requires a round trip.
const serverRes = await write.server
if (serverRes.type === 'error') {
throw new Error(
`Mutator failed on server`,
serverRes.error
)
throw new Error(`Mutator failed on server`, {
cause: serverRes.error
})
}

// issue-123 is written to server and any results are
// synced to this client.
// read2 could potentially be undefined here, for example if the
// server mutator rejected the write.
// The server acknowledged the mutation, but its Postgres changes
// may not have replicated to this client yet. This read can still
// reflect optimistic rather than authoritative state.
const read2 = await zero.run(
queries.issue.byId('issue-123').one()
)
```

If the client-side mutator fails, the `.server` promise is also rejected with the same error. You don't have to listen to both promises, the server promise covers both cases.
If the client-side mutator fails, `.server` also resolves to an error result. Awaiting `.server` therefore covers both client- and server-side failures.

> **Returning data from mutators**: There is not yet a way to return data from mutators in the success case. [Let us know](https://discord.rocicorp.dev/)if you need this.

Expand Down
Loading