From bc7c56f22640ca2061f6adf3968795134ff52046 Mon Sep 17 00:00:00 2001 From: Sriket Komali Date: Tue, 7 Apr 2026 14:14:09 -0400 Subject: [PATCH 1/3] docs: document ZERO_APP_ID replication slot pitfall for local dev Running zero-cache locally against the same Postgres as a deployed instance (e.g. via a monorepo dev script like turbo dev) causes the local instance to steal the exclusive replication slot, crashing production. Setting ZERO_APP_ID=zero_dev locally gives each environment its own slot so both can run simultaneously without conflict. Added a warning callout to the App ID section in zero-cache-config and a new "Local Development Against a Shared Database" section in deployment. Co-Authored-By: Claude Sonnet 4.6 --- contents/docs/deployment.mdx | 23 +++++++++++++++++++++++ contents/docs/zero-cache-config.mdx | 19 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/contents/docs/deployment.mdx b/contents/docs/deployment.mdx index 3c7fdee..a170bb8 100644 --- a/contents/docs/deployment.mdx +++ b/contents/docs/deployment.mdx @@ -351,3 +351,26 @@ To upgrade Zero to a new major version, first deploy the new zero-cache, then th ### Configuration The zero-cache image is configured via environment variables. See [zero-cache Config](/docs/zero-cache-config) for available options. + +## Local Development Against a Shared Database + +If you run `zero-cache` locally pointing at the same upstream Postgres as a +deployed instance (e.g. to test against a staging or production database), you +**must** set a distinct [`ZERO_APP_ID`](/docs/zero-cache-config#app-id) in +your local environment: + +```bash +# local .env only +ZERO_APP_ID=zero_dev +``` + +Without this, the local instance will call `pg_terminate_backend` to steal the +shared replication slot, crashing the deployed instance. This includes monorepo +dev scripts (e.g. `turbo dev`) that start zero-cache as a workspace package — +if `ZERO_UPSTREAM_DB` points at a shared database, local zero-cache will evict +the deployed one. Every eviction also triggers a full re-sync of all replicated +tables from Postgres. + +Leave `ZERO_APP_ID` unset in production (defaults to `zero`). The two +environments then hold independent replication slots and can run simultaneously — +no separate dev database needed. diff --git a/contents/docs/zero-cache-config.mdx b/contents/docs/zero-cache-config.mdx index c0add5e..1e8e343 100644 --- a/contents/docs/zero-cache-config.mdx +++ b/contents/docs/zero-cache-config.mdx @@ -46,6 +46,25 @@ flag: `--app-id`
env: `ZERO_APP_ID`
default: `zero` + + If you run `zero-cache` locally against the same Postgres database as a + deployed instance, you **must** set a different `ZERO_APP_ID` locally (e.g. + `ZERO_APP_ID=zero_dev`). Two instances sharing the same App ID fight over the + exclusive Postgres replication slot — the newer instance calls + `pg_terminate_backend` to steal the slot, crashing the other. This applies to + any monorepo dev script (e.g. `turbo dev`) that starts zero-cache as part of + the workspace: if `ZERO_UPSTREAM_DB` points at a shared database, local + zero-cache will evict the deployed instance. + + With distinct App IDs, local and production use separate replication slots + (`zero_dev_0_...` vs `zero_0_...`) and can run simultaneously against the + same Postgres without conflict. You do not need a separate dev database. + + ### 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. From a232204fb6a4ed1a9d0d078a30fcbefd5b9de79f Mon Sep 17 00:00:00 2001 From: Sriket Komali Date: Tue, 7 Apr 2026 14:19:55 -0400 Subject: [PATCH 2/3] fix: clarify re-sync is triggered by restart after crash, not eviction itself --- contents/docs/deployment.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contents/docs/deployment.mdx b/contents/docs/deployment.mdx index a170bb8..8ebbb50 100644 --- a/contents/docs/deployment.mdx +++ b/contents/docs/deployment.mdx @@ -368,8 +368,9 @@ Without this, the local instance will call `pg_terminate_backend` to steal the shared replication slot, crashing the deployed instance. This includes monorepo dev scripts (e.g. `turbo dev`) that start zero-cache as a workspace package — if `ZERO_UPSTREAM_DB` points at a shared database, local zero-cache will evict -the deployed one. Every eviction also triggers a full re-sync of all replicated -tables from Postgres. +the deployed one. When the deployed instance is then restarted or redeployed, it +must perform a full re-sync of all replicated tables from Postgres, incurring +unnecessary egress costs. Leave `ZERO_APP_ID` unset in production (defaults to `zero`). The two environments then hold independent replication slots and can run simultaneously — From 39b40bd8d499b6f7df11bfe57377f9ddfdc6bf5c Mon Sep 17 00:00:00 2001 From: Sriket Komali Date: Tue, 7 Apr 2026 14:21:27 -0400 Subject: [PATCH 3/3] remove references to monorepo dev scripts --- contents/docs/deployment.mdx | 9 +++------ contents/docs/zero-cache-config.mdx | 5 +---- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/contents/docs/deployment.mdx b/contents/docs/deployment.mdx index 8ebbb50..ccde5e1 100644 --- a/contents/docs/deployment.mdx +++ b/contents/docs/deployment.mdx @@ -365,12 +365,9 @@ ZERO_APP_ID=zero_dev ``` Without this, the local instance will call `pg_terminate_backend` to steal the -shared replication slot, crashing the deployed instance. This includes monorepo -dev scripts (e.g. `turbo dev`) that start zero-cache as a workspace package — -if `ZERO_UPSTREAM_DB` points at a shared database, local zero-cache will evict -the deployed one. When the deployed instance is then restarted or redeployed, it -must perform a full re-sync of all replicated tables from Postgres, incurring -unnecessary egress costs. +shared replication slot, crashing the deployed instance. When the deployed +instance is then restarted or redeployed, it must perform a full re-sync of all +replicated tables from Postgres, incurring unnecessary egress costs. Leave `ZERO_APP_ID` unset in production (defaults to `zero`). The two environments then hold independent replication slots and can run simultaneously — diff --git a/contents/docs/zero-cache-config.mdx b/contents/docs/zero-cache-config.mdx index 1e8e343..bffccfe 100644 --- a/contents/docs/zero-cache-config.mdx +++ b/contents/docs/zero-cache-config.mdx @@ -55,10 +55,7 @@ default: `zero` deployed instance, you **must** set a different `ZERO_APP_ID` locally (e.g. `ZERO_APP_ID=zero_dev`). Two instances sharing the same App ID fight over the exclusive Postgres replication slot — the newer instance calls - `pg_terminate_backend` to steal the slot, crashing the other. This applies to - any monorepo dev script (e.g. `turbo dev`) that starts zero-cache as part of - the workspace: if `ZERO_UPSTREAM_DB` points at a shared database, local - zero-cache will evict the deployed instance. + `pg_terminate_backend` to steal the slot, crashing the other. With distinct App IDs, local and production use separate replication slots (`zero_dev_0_...` vs `zero_0_...`) and can run simultaneously against the