Skip to content

pgEdge Spock 5.0.4 + snowflake extensions#42814

Open
nightness wants to merge 20 commits intosupabase:masterfrom
Brainwires:master
Open

pgEdge Spock 5.0.4 + snowflake extensions#42814
nightness wants to merge 20 commits intosupabase:masterfrom
Brainwires:master

Conversation

@nightness
Copy link
Copy Markdown

@nightness nightness commented Feb 14, 2026

This pull request adds support for bi-directional, multi-master PostgreSQL replication using the Spock extension, along with distributed unique ID generation via the Snowflake extension. The changes introduce a custom PostgreSQL image, configuration files, initialization scripts, and comprehensive documentation to enable active-active replication between Supabase instances. The setup is optional: if not configured, the system works as a standard Supabase install.

The most important changes are:

Bi-Directional Replication and Distributed ID Support

  • Switched the default PostgreSQL image in docker-compose.yml to a custom Spock-enabled image, and mounted new configuration and initialization scripts for Spock and Snowflake. [1] [2]
  • Added initialization SQL scripts for the Snowflake extension (97-snowflake.sql), Spock extension (99-spock.sql), and replication user setup (98-spock-replication.sql). [1] [2] [3]
  • Included a new setup script (spock-setup.sh) to automate the process of establishing bi-directional replication between nodes.

Configuration and Security

  • Added a new pg_hba.conf with pre-configured replication and access rules, and updated environment variables in .env.example for Spock and Snowflake configuration (including replication password and WAL settings). [1] [2]
  • Updated the PostgreSQL container command to set the snowflake.node parameter from the environment for unique node IDs.

Documentation

  • Expanded docker/README.md with detailed instructions for enabling Spock-based replication, Snowflake ID usage, DDL replication, secure cross-network setup with Cloudflare Tunnels, and important operational notes. [1] [2]

These changes enable robust multi-region, multi-node deployments with conflict-free primary keys and seamless schema replication, while remaining backward compatible for single-node setups.## I have read the CONTRIBUTING.md file.

YES

What kind of change does this PR introduce?

Feature

What is the current behavior?

Missing pgEdge extensions Spock and Snowflake

What is the new behavior?

Added

Additional context

New Postgres Docker Image
Related PR in Supabase's postgres project

Summary by CodeRabbit

  • New Features

    • Bi-directional database replication with Spock 5.x
    • Snowflake-style distributed unique ID generation
    • Cross-network replication support (e.g., via tunneling)
    • Automatic DDL and table replication with streamlined setup scripts
  • Documentation

    • Detailed Docker-based guides for multi-node replication setup and verification

nightness and others added 16 commits January 26, 2026 09:32
- Add postgresql-spock.conf with optimized PostgreSQL settings for Spock
- Add cloudflare/ directory with tunnel configuration for cross-node replication
- Add SPOCK-SETUP.md with complete setup and deployment guide

See SPOCK-SETUP.md for configuration instructions and the companion
supabase-postgres-spock image required for multi-master replication.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Config file updates:
- docker-compose.yml: Add container naming with COMPOSE_PROJECT_NAME,
  SITE_URL_PATTERN/REALTIME_CONTAINER_NAME support, MCP service with
  profile, Spock configuration (commented), LOGFLARE_MIN_CLUSTER_SIZE
- kong.yml: Add CORS configs with SITE_URL_PATTERN on all routes,
  REALTIME_CONTAINER_NAME variable, MCP routes for dedicated service
- vector.yml: Add health filter for Realtime, functions_logs transform,
  container name patterns for both naming conventions
- .env.example: Add SITE_URL_PATTERN, REALTIME_CONTAINER_NAME, POOLER_DB_PORT

Documentation updates:
- SPOCK-SETUP.md: Replace brainwires-specific hostnames with generic
  example.com placeholders, update container names and ports
- cloudflare/.env.example: Replace brainwires hostnames with generic examples
- cloudflare/docker-compose.yml: Update to generic network names and ports

All brainwires-specific references have been removed or abstracted.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- cloudflare/docker-compose.yml: CF_PG_PORT (default: 35432)
- docker-compose.yml: ANALYTICS_PORT (default: 4000)
- .env.example: Add CF_PG_PORT and ANALYTICS_PORT variables
- SPOCK-SETUP.md: Update port references to use ${CF_PG_PORT}

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change db service to use Spock-enabled image by default
- Replication is optional (disabled when REPLICA_HOST_IP is empty)
- Fix command format to use standard postgres args (not bash script)
- Make SITE_URL_PATTERN required with default .* (Kong fails on empty)
- Add DB_PORT and LOG_MIN_MESSAGES to .env.example

The Spock image works as a drop-in replacement for standard PostgreSQL
when replication is not configured.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The variable name was misleading as it implied Cloudflare-specific usage,
but the port can be used with any tunneling solution or direct connections.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Consistent naming with PG_REPLICATION_PORT, and not Cloudflare-specific.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Image now available at: ghcr.io/brainwires/supabase-postgres-spock:15

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add spock--3.1.8.sql: Extension SQL missing from Nix build, now mounted
- Add 99-spock.sql: Migration to create Spock extension on init
- Update 99-replication-init.sh: Only handles user/pg_hba (extension via SQL)
- Add spock-setup.sh: Complete bi-directional replication setup script
  - Creates local node, remote interface, subscription
  - Applies Spock 3.1.8 manual fixes (origin, sync status, slot)
  - Tests connectivity and verifies replication status

Usage: After both databases are up, run on each node:
  docker exec <container> bash /spock-setup.sh <node_name> <remote_host> <remote_port>

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove SPOCK-SETUP.md, integrate docs into README.md
- Add pg_hba.conf with pre-configured replication access
- Add 98-spock-replication.sql to create replicator user automatically
- Add event trigger to auto-add tables to replication set
- Update spock-setup.sh to include ddl_sql replication set
- Add Spock DDL replication settings to postgresql-spock.conf
- Remove obsolete 99-replication-init.sh (functionality moved to SQL migrations)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Explain that the CLI is required for automatic DDL wrapping in
spock.replicate_ddl(). Document the manual alternative for users
who cannot use the CLI.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add environment variables for Google OAuth integration:
- ENABLE_GOOGLE_SIGNUP to toggle the feature
- OAUTH_GOOGLE_CLIENT_ID, OAUTH_GOOGLE_SECRET, OAUTH_GOOGLE_REDIRECT_URI

Disabled by default; users can enable by setting credentials in .env.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Snowflake distributed ID generator for conflict-free multi-node
primary keys, eliminating the need for sequence offsets. Update
documentation to reflect Spock 5.0.4's automatic DDL replication
and remove outdated manual wrapping requirements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve conflicts in docker/docker-compose.yml:
- Keep ${COMPOSE_PROJECT_NAME:-supabase} container naming pattern
- Accept upstream's newer image versions
- Preserve our Spock/Snowflake additions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove unrelated changes that crept into the PR:
- Remove Cloudflare tunnel config (personal infrastructure)
- Remove Google OAuth additions
- Remove COMPOSE_PROJECT_NAME container renaming
- Remove CORS, MCP, analytics port, log level changes
- Revert kong.yml and vector.yml to upstream
- Keep only Spock replication and Snowflake ID support

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…p instructions

- Add Cloudflare Tunnels section for secure cross-network replication
- Fix spock-setup.sh instructions to explain two-pass approach
- Remove COMPOSE_PROJECT_NAME reference (not in docker-compose.yml)
- Use correct container name (supabase-db) in examples

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nightness nightness requested review from a team as code owners February 14, 2026 14:47
@github-actions
Copy link
Copy Markdown
Contributor

Thanks for contributing to Supabase! ❤️ Our team will review your PR.

A few tips for a smoother review process:

  • If you have a local version of the repo, run pnpm run format to make sure formatting checks pass.
  • Once we've reviewed your PR, please don't trivially merge master (don't click Update branch if there are no merge conflicts to be fixed). This invalidates any pre-merge checks we've run.

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
design-system Ready Ready Preview, Comment Feb 16, 2026 8:30am

Request Review

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 14, 2026

Someone is attempting to deploy a commit to the Supabase Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 14, 2026

Walkthrough

Adds Spock bi-directional replication to the Docker setup: swaps the Postgres image for a Spock-enabled image, adds Spock/Snowflake config and init scripts, adds a replication user and pg_hba rules, provides an automated spock-setup script, and documents setup and operational notes in docker/README.md and .env.example.

Changes

Cohort / File(s) Summary
Env & Docs
docker/.env.example, docker/README.md
Adds Spock/Snowflake environment variables and explanatory comments; adds a comprehensive Bi-Directional Replication with Spock section covering configuration, Snowflake ID usage, two-node setup, cross-network options, and operational notes.
Compose & Container
docker/docker-compose.yml
Replaces Postgres image with a Spock-enabled image, adds extra_hosts mapping, mounts Spock-specific config and init files, includes spock-setup.sh for post-start setup, and injects REPLICATION_PASSWORD and SNOWFLAKE_NODE handling.
Postgres Auth
docker/volumes/db/pg_hba.conf
New pg_hba.conf allowing local trust + scram-sha-256 for host connections and explicit replication access for spock_replicator.
Postgres Config
docker/volumes/db/postgresql-spock.conf
New Postgres config enabling many shared_preload_libraries (including spock), WAL/replication settings, includes for external config, and Spock-specific DDL replication flags.
Replication User Init
docker/volumes/db/98-spock-replication.sh
New init script that creates/ensures spock_replicator user with replication and schema/table privileges and sets default privileges for future tables.
Snowflake Init
docker/volumes/db/96-snowflake.sql
New SQL init to create the snowflake extension (requires SNOWFLAKE_NODE env to be provided at runtime).
Spock Init & Triggers
docker/volumes/db/97-spock.sql
New SQL init to create the spock extension and add an event trigger + function to auto-add newly created tables to the default repset for DDL replication.
Replication Automation
docker/volumes/db/spock-setup.sh
New Bash automation that configures local/remote Spock nodes, interfaces, subscriptions, replication slots/origins, polls for replication status, and emits success/warning messages.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Operator
participant spock_setup as spock-setup.sh
participant LocalDB as Local Postgres (Spock)
participant RemoteDB as Remote Postgres (Spock)

Operator->>spock_setup: invoke (local_node, remote_host, remote_port)
spock_setup->>LocalDB: check/create local node
spock_setup->>RemoteDB: validate connectivity / query remote node
RemoteDB-->>spock_setup: remote node info
spock_setup->>LocalDB: register remote node & add interface
spock_setup->>RemoteDB: create subscription / replication slot (if needed)
RemoteDB-->>spock_setup: slot created / status
spock_setup->>LocalDB: create replication origin / start subscription
LocalDB-->>spock_setup: subscription running
spock_setup->>Operator: report success / warnings

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title "pgEdge Spock 5.0.4 + snowflake extensions" is concise and accurately summarizes the main feature additions in this PR.
Description check ✅ Passed The description follows the template with all required sections completed: reads CONTRIBUTING.md (YES), type (Feature), current behavior, new behavior, and additional context with supporting links.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into master

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@docker/docker-compose.yml`:
- Around line 471-473: The command array contains an invalid trailing comma
after the "log_min_messages=fatal" string which breaks YAML parsing; update the
command sequence around the "-c" flag and the
"snowflake.node=${SNOWFLAKE_NODE:-1}" entry so items are valid YAML sequence
elements (remove the stray comma and ensure each array item is on its own line
without trailing commas), locating the entries by the literal strings
"log_min_messages=fatal", "-c", and "snowflake.node=${SNOWFLAKE_NODE:-1}" and
fixing the punctuation accordingly.
- Around line 403-407: The docker-compose service currently uses an unpinned
image tag "ghcr.io/brainwires/supabase-postgres-spock:15" which makes builds
non-reproducible; update the image reference in docker-compose.yml to the
corresponding immutable digest by replacing the tag with the exact digest (e.g.,
"ghcr.io/brainwires/supabase-postgres-spock@sha256:<digest>") so the service
image is pinned; obtain the digest using docker manifest inspect as suggested
and update the image field for that Postgres/Spock service.

In `@docker/volumes/db/98-spock-replication.sql`:
- Around line 9-12: The CREATE USER statement currently hardcodes the
spock_replicator password (CREATE USER spock_replicator WITH REPLICATION LOGIN
PASSWORD 'spock_replication_password_change_me'); remove the embedded secret and
replace it with a deploy-time mechanism: either a required placeholder token
(e.g. '<REPLICATION_PASSWORD>') that CI/ops must replace, or switch to
generating a random password at container startup and injecting it into the init
script (and emitting it to a secure log/file), or use an environment-driven
substitution approach; also add a prominent warning in the PR/README that the
replication password must be changed prior to any network exposure and document
the chosen replacement mechanism.

In `@docker/volumes/db/postgresql-spock.conf`:
- Around line 297-302: The config key max_replication_slots in
postgresql-spock.conf (currently 5) disagrees with the environment variable
MAX_REPLICATION_SLOTS in .env.example (10); fix by either updating
postgresql-spock.conf to set max_replication_slots = 10 to match
MAX_REPLICATION_SLOTS, or remove/align the MAX_REPLICATION_SLOTS entry from
.env.example; if you intend the env var to be authoritative, instead convert
postgresql-spock.conf to use template substitution (envsubst or similar) so
MAX_REPLICATION_SLOTS is injected at container start.

In `@docker/volumes/db/spock-setup.sh`:
- Around line 108-125: The script does not validate SUB_ID after calling
spock.sub_create, so if creation returns empty or fails subsequent queries (like
fetching sub_slot_name) will break; after assigning SUB_ID (from
spock.sub_create or the SELECT for existing subscription) trim/check it and if
it's empty or invalid, log a clear error including SUB_NAME and
provider/response context and exit non‑zero; specifically add a check
immediately after the SUB_ID assignment (the variable SUB_ID created around
spock.sub_create and in the else branch) to verify non-empty (and optionally
numeric), and only proceed to query sub_slot_name/SLOT_NAME if SUB_ID is valid,
otherwise echo an error and exit 1.
🧹 Nitpick comments (6)
docker/volumes/db/98-spock-replication.sql (1)

24-25: Default privileges may not apply to all tables.

ALTER DEFAULT PRIVILEGES only affects tables created by the role that executed this statement (likely postgres or supabase_admin). Tables created by other roles (e.g., application users) won't automatically grant SELECT to spock_replicator.

If replication should cover all tables regardless of creator, you may need to either:

  • Run this as each role that creates tables, or
  • Periodically grant SELECT on new tables via a maintenance script
docker/volumes/db/spock-setup.sh (1)

170-178: Fixed sleep may be insufficient for subscription startup.

A 3-second sleep is arbitrary and may not be enough on slower systems or under load. Consider polling the status with a timeout instead.

♻️ Suggested polling approach
-# Wait for subscription to come up
-echo "Waiting for subscription to start..."
-sleep 3
-
-# Step 8: Verify status
-echo ""
-echo "Step 8: Verifying subscription status..."
-STATUS=$(psql -U supabase_admin -d postgres -tAc "SELECT status FROM spock.sub_show_status() WHERE subscription_name = '$SUB_NAME';")
+# Wait for subscription to come up (poll with timeout)
+echo "Waiting for subscription to start..."
+TIMEOUT=30
+ELAPSED=0
+while [ $ELAPSED -lt $TIMEOUT ]; do
+    STATUS=$(psql -U supabase_admin -d postgres -tAc "SELECT status FROM spock.sub_show_status() WHERE subscription_name = '$SUB_NAME';")
+    if [ "$STATUS" = "replicating" ]; then
+        break
+    fi
+    sleep 2
+    ELAPSED=$((ELAPSED + 2))
+done
+
+echo ""
+echo "Step 8: Verifying subscription status..."
docker/volumes/db/97-snowflake.sql (1)

1-5: Minor documentation inconsistency.

The comment states snowflake.node must be set in postgresql-spock.conf, but based on the docker-compose configuration, it's actually set via the PostgreSQL command-line argument (-c snowflake.node=${SNOWFLAKE_NODE:-1}), which reads from the .env file.

Consider updating the comment:

 -- Snowflake Extension Initialization
 -- Creates the snowflake extension for distributed unique ID generation
--- Required: snowflake.node must be set in postgresql-spock.conf (1-1023, unique per node)
+-- Required: snowflake.node must be set via SNOWFLAKE_NODE in .env (1-1023, unique per node)
docker/README.md (2)

134-138: Add language specifier to fenced code block.

Per static analysis, this code block should have a language specified for proper syntax highlighting.

-```
+```ini
 spock.enable_ddl_replication = on
 spock.include_ddl_repset = on
 spock.allow_ddl_from_functions = on

---

`198-211`: **Add language specifier to ASCII diagram block.**

For consistency and to satisfy linters, add a language specifier (e.g., `text`) to the ASCII diagram code block.

```diff
-```
+```text
 PRIMARY (server-a)                          STANDBY (server-b)
 ┌─────────────────────┐                     ┌─────────────────────┐
docker/docker-compose.yml (1)

410-419: Clarify migration ordering to avoid accidental reordering.

Line 415 maps 97-snowflake.sql96-snowflake.sql and Line 417 maps 99-spock.sql97-spock.sql, which makes ordering non-obvious and easy to break when new migrations are added. Consider aligning target filenames with source numbering or adding a brief comment explaining the intentional ordering.

Comment thread docker/docker-compose.yml
Comment thread docker/docker-compose.yml Outdated
Comment thread docker/volumes/db/98-spock-replication.sql Outdated
Comment thread docker/volumes/db/postgresql-spock.conf
Comment thread docker/volumes/db/spock-setup.sh
nightness and others added 2 commits February 14, 2026 15:03
- Fix YAML: convert command from flow sequence to block style (comments
  are invalid in flow sequences)
- Pin image to immutable digest instead of mutable :15 tag
- Remove hardcoded replication password: convert 98-spock-replication.sql
  to .sh script that reads REPLICATION_PASSWORD from environment
- Fix max_replication_slots=5 in postgresql-spock.conf to match
  MAX_REPLICATION_SLOTS=10 in .env.example
- Add SUB_ID validation in spock-setup.sh to fail fast with clear error
  if subscription creation fails
- Strengthen replication password warning in README

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix snowflake.sql comment: node set via SNOWFLAKE_NODE in .env, not conf
- Add note about ALTER DEFAULT PRIVILEGES scope limitation
- Replace fixed sleep with polling loop (30s timeout) for subscription status
- Add language specifiers to README code blocks (ini, text)
- Align init script filenames with target migration numbering (96, 97, 98)
- Add ordering comment in docker-compose.yml volume mounts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@docker/README.md`:
- Around line 133-138: Add language identifiers to the fenced code blocks in the
README: update the config snippet fence to use "conf" for the block containing
the postgresql-spock.conf settings (spock.enable_ddl_replication,
spock.include_ddl_repset, spock.allow_ddl_from_functions) and set the ASCII
diagram block to use "text" (the diagram showing PRIMARY (server-a) / STANDBY
(server-b)); apply the same changes to the other fenced block referenced around
lines 198-211 so markdownlint MD040 is satisfied.

In `@docker/volumes/db/98-spock-replication.sh`:
- Around line 11-18: The script directly interpolates REPLICATION_PASSWORD into
the CREATE USER statement (CREATE USER spock_replicator WITH REPLICATION LOGIN
PASSWORD '${REPLICATION_PASSWORD}'), which breaks on quotes and allows
injection; instead, pass the password into psql via psql -v (e.g., REPL_PW) and
use format('%L', :'REPL_PW') (or quote_literal(:'REPL_PW')) inside the DO block
to safely literal-quote the value when constructing the CREATE USER command for
spock_replicator, avoiding direct shell interpolation.

Comment thread docker/README.md
Comment thread docker/volumes/db/98-spock-replication.sh
@nightness
Copy link
Copy Markdown
Author

Actionable comments posted: 5

🤖 Fix all issues with AI agents

In `@docker/docker-compose.yml`:
- Around line 471-473: The command array contains an invalid trailing comma
after the "log_min_messages=fatal" string which breaks YAML parsing; update the
command sequence around the "-c" flag and the
"snowflake.node=${SNOWFLAKE_NODE:-1}" entry so items are valid YAML sequence
elements (remove the stray comma and ensure each array item is on its own line
without trailing commas), locating the entries by the literal strings
"log_min_messages=fatal", "-c", and "snowflake.node=${SNOWFLAKE_NODE:-1}" and
fixing the punctuation accordingly.
- Around line 403-407: The docker-compose service currently uses an unpinned
image tag "ghcr.io/brainwires/supabase-postgres-spock:15" which makes builds
non-reproducible; update the image reference in docker-compose.yml to the
corresponding immutable digest by replacing the tag with the exact digest (e.g.,
"ghcr.io/brainwires/supabase-postgres-spock@sha256:<digest>") so the service
image is pinned; obtain the digest using docker manifest inspect as suggested
and update the image field for that Postgres/Spock service.

In `@docker/volumes/db/98-spock-replication.sql`:
- Around line 9-12: The CREATE USER statement currently hardcodes the
spock_replicator password (CREATE USER spock_replicator WITH REPLICATION LOGIN
PASSWORD 'spock_replication_password_change_me'); remove the embedded secret and
replace it with a deploy-time mechanism: either a required placeholder token
(e.g. '<REPLICATION_PASSWORD>') that CI/ops must replace, or switch to
generating a random password at container startup and injecting it into the init
script (and emitting it to a secure log/file), or use an environment-driven
substitution approach; also add a prominent warning in the PR/README that the
replication password must be changed prior to any network exposure and document
the chosen replacement mechanism.

In `@docker/volumes/db/postgresql-spock.conf`:
- Around line 297-302: The config key max_replication_slots in
postgresql-spock.conf (currently 5) disagrees with the environment variable
MAX_REPLICATION_SLOTS in .env.example (10); fix by either updating
postgresql-spock.conf to set max_replication_slots = 10 to match
MAX_REPLICATION_SLOTS, or remove/align the MAX_REPLICATION_SLOTS entry from
.env.example; if you intend the env var to be authoritative, instead convert
postgresql-spock.conf to use template substitution (envsubst or similar) so
MAX_REPLICATION_SLOTS is injected at container start.

In `@docker/volumes/db/spock-setup.sh`:
- Around line 108-125: The script does not validate SUB_ID after calling
spock.sub_create, so if creation returns empty or fails subsequent queries (like
fetching sub_slot_name) will break; after assigning SUB_ID (from
spock.sub_create or the SELECT for existing subscription) trim/check it and if
it's empty or invalid, log a clear error including SUB_NAME and
provider/response context and exit non‑zero; specifically add a check
immediately after the SUB_ID assignment (the variable SUB_ID created around
spock.sub_create and in the else branch) to verify non-empty (and optionally
numeric), and only proceed to query sub_slot_name/SLOT_NAME if SUB_ID is valid,
otherwise echo an error and exit 1.

🧹 Nitpick comments (6)

docker/volumes/db/98-spock-replication.sql (1)> 24-25: Default privileges may not apply to all tables.

ALTER DEFAULT PRIVILEGES only affects tables created by the role that executed this statement (likely postgres or supabase_admin). Tables created by other roles (e.g., application users) won't automatically grant SELECT to spock_replicator.
If replication should cover all tables regardless of creator, you may need to either:

  • Run this as each role that creates tables, or
  • Periodically grant SELECT on new tables via a maintenance script

docker/volumes/db/spock-setup.sh (1)> 170-178: Fixed sleep may be insufficient for subscription startup.

A 3-second sleep is arbitrary and may not be enough on slower systems or under load. Consider polling the status with a timeout instead.

♻️ Suggested polling approach

-# Wait for subscription to come up
-echo "Waiting for subscription to start..."
-sleep 3
-
-# Step 8: Verify status
-echo ""
-echo "Step 8: Verifying subscription status..."
-STATUS=$(psql -U supabase_admin -d postgres -tAc "SELECT status FROM spock.sub_show_status() WHERE subscription_name = '$SUB_NAME';")
+# Wait for subscription to come up (poll with timeout)
+echo "Waiting for subscription to start..."
+TIMEOUT=30
+ELAPSED=0
+while [ $ELAPSED -lt $TIMEOUT ]; do
+    STATUS=$(psql -U supabase_admin -d postgres -tAc "SELECT status FROM spock.sub_show_status() WHERE subscription_name = '$SUB_NAME';")
+    if [ "$STATUS" = "replicating" ]; then
+        break
+    fi
+    sleep 2
+    ELAPSED=$((ELAPSED + 2))
+done
+
+echo ""
+echo "Step 8: Verifying subscription status..."

docker/volumes/db/97-snowflake.sql (1)> 1-5: Minor documentation inconsistency.

The comment states snowflake.node must be set in postgresql-spock.conf, but based on the docker-compose configuration, it's actually set via the PostgreSQL command-line argument (-c snowflake.node=${SNOWFLAKE_NODE:-1}), which reads from the .env file.
Consider updating the comment:

 -- Snowflake Extension Initialization
 -- Creates the snowflake extension for distributed unique ID generation
--- Required: snowflake.node must be set in postgresql-spock.conf (1-1023, unique per node)
+-- Required: snowflake.node must be set via SNOWFLAKE_NODE in .env (1-1023, unique per node)

docker/README.md (2)> 134-138: Add language specifier to fenced code block.

Per static analysis, this code block should have a language specified for proper syntax highlighting.

-```
+```ini
 spock.enable_ddl_replication = on
 spock.include_ddl_repset = on
 spock.allow_ddl_from_functions = on

---

`198-211`: **Add language specifier to ASCII diagram block.**

For consistency and to satisfy linters, add a language specifier (e.g., `text`) to the ASCII diagram code block.

```diff
-```
+```text
 PRIMARY (server-a)                          STANDBY (server-b)
 ┌─────────────────────┐                     ┌─────────────────────┐

docker/docker-compose.yml (1)> 410-419: Clarify migration ordering to avoid accidental reordering.

Line 415 maps 97-snowflake.sql96-snowflake.sql and Line 417 maps 99-spock.sql97-spock.sql, which makes ordering non-obvious and easy to break when new migrations are added. Consider aligning target filenames with source numbering or adding a brief comment explaining the intentional ordering.

Should be resolved

…tion script

- Change README code block from ini to conf for postgresql settings
- Fix SQL injection: pass password via psql -v variable and use
  format('%L', :'REPL_PW') instead of shell interpolation
- Use quoted heredoc (<<'EOSQL') to prevent shell expansion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nightness
Copy link
Copy Markdown
Author

Actionable comments posted: 5
🤖 Fix all issues with AI agents

In `@docker/docker-compose.yml`:
- Around line 471-473: The command array contains an invalid trailing comma
after the "log_min_messages=fatal" string which breaks YAML parsing; update the
command sequence around the "-c" flag and the
"snowflake.node=${SNOWFLAKE_NODE:-1}" entry so items are valid YAML sequence
elements (remove the stray comma and ensure each array item is on its own line
without trailing commas), locating the entries by the literal strings
"log_min_messages=fatal", "-c", and "snowflake.node=${SNOWFLAKE_NODE:-1}" and
fixing the punctuation accordingly.
- Around line 403-407: The docker-compose service currently uses an unpinned
image tag "ghcr.io/brainwires/supabase-postgres-spock:15" which makes builds
non-reproducible; update the image reference in docker-compose.yml to the
corresponding immutable digest by replacing the tag with the exact digest (e.g.,
"ghcr.io/brainwires/supabase-postgres-spock@sha256:<digest>") so the service
image is pinned; obtain the digest using docker manifest inspect as suggested
and update the image field for that Postgres/Spock service.

In `@docker/volumes/db/98-spock-replication.sql`:
- Around line 9-12: The CREATE USER statement currently hardcodes the
spock_replicator password (CREATE USER spock_replicator WITH REPLICATION LOGIN
PASSWORD 'spock_replication_password_change_me'); remove the embedded secret and
replace it with a deploy-time mechanism: either a required placeholder token
(e.g. '<REPLICATION_PASSWORD>') that CI/ops must replace, or switch to
generating a random password at container startup and injecting it into the init
script (and emitting it to a secure log/file), or use an environment-driven
substitution approach; also add a prominent warning in the PR/README that the
replication password must be changed prior to any network exposure and document
the chosen replacement mechanism.

In `@docker/volumes/db/postgresql-spock.conf`:
- Around line 297-302: The config key max_replication_slots in
postgresql-spock.conf (currently 5) disagrees with the environment variable
MAX_REPLICATION_SLOTS in .env.example (10); fix by either updating
postgresql-spock.conf to set max_replication_slots = 10 to match
MAX_REPLICATION_SLOTS, or remove/align the MAX_REPLICATION_SLOTS entry from
.env.example; if you intend the env var to be authoritative, instead convert
postgresql-spock.conf to use template substitution (envsubst or similar) so
MAX_REPLICATION_SLOTS is injected at container start.

In `@docker/volumes/db/spock-setup.sh`:
- Around line 108-125: The script does not validate SUB_ID after calling
spock.sub_create, so if creation returns empty or fails subsequent queries (like
fetching sub_slot_name) will break; after assigning SUB_ID (from
spock.sub_create or the SELECT for existing subscription) trim/check it and if
it's empty or invalid, log a clear error including SUB_NAME and
provider/response context and exit non‑zero; specifically add a check
immediately after the SUB_ID assignment (the variable SUB_ID created around
spock.sub_create and in the else branch) to verify non-empty (and optionally
numeric), and only proceed to query sub_slot_name/SLOT_NAME if SUB_ID is valid,
otherwise echo an error and exit 1.

🧹 Nitpick comments (6)

docker/volumes/db/98-spock-replication.sql (1)> 24-25: Default privileges may not apply to all tables.

ALTER DEFAULT PRIVILEGES only affects tables created by the role that executed this statement (likely postgres or supabase_admin). Tables created by other roles (e.g., application users) won't automatically grant SELECT to spock_replicator.
If replication should cover all tables regardless of creator, you may need to either:

  • Run this as each role that creates tables, or
  • Periodically grant SELECT on new tables via a maintenance script

docker/volumes/db/spock-setup.sh (1)> 170-178: Fixed sleep may be insufficient for subscription startup.

A 3-second sleep is arbitrary and may not be enough on slower systems or under load. Consider polling the status with a timeout instead.
♻️ Suggested polling approach

-# Wait for subscription to come up
-echo "Waiting for subscription to start..."
-sleep 3
-
-# Step 8: Verify status
-echo ""
-echo "Step 8: Verifying subscription status..."
-STATUS=$(psql -U supabase_admin -d postgres -tAc "SELECT status FROM spock.sub_show_status() WHERE subscription_name = '$SUB_NAME';")
+# Wait for subscription to come up (poll with timeout)
+echo "Waiting for subscription to start..."
+TIMEOUT=30
+ELAPSED=0
+while [ $ELAPSED -lt $TIMEOUT ]; do
+    STATUS=$(psql -U supabase_admin -d postgres -tAc "SELECT status FROM spock.sub_show_status() WHERE subscription_name = '$SUB_NAME';")
+    if [ "$STATUS" = "replicating" ]; then
+        break
+    fi
+    sleep 2
+    ELAPSED=$((ELAPSED + 2))
+done
+
+echo ""
+echo "Step 8: Verifying subscription status..."

docker/volumes/db/97-snowflake.sql (1)> 1-5: Minor documentation inconsistency.

The comment states snowflake.node must be set in postgresql-spock.conf, but based on the docker-compose configuration, it's actually set via the PostgreSQL command-line argument (-c snowflake.node=${SNOWFLAKE_NODE:-1}), which reads from the .env file.
Consider updating the comment:

 -- Snowflake Extension Initialization
 -- Creates the snowflake extension for distributed unique ID generation
--- Required: snowflake.node must be set in postgresql-spock.conf (1-1023, unique per node)
+-- Required: snowflake.node must be set via SNOWFLAKE_NODE in .env (1-1023, unique per node)

docker/README.md (2)> 134-138: Add language specifier to fenced code block.

Per static analysis, this code block should have a language specified for proper syntax highlighting.

-```
+```ini
 spock.enable_ddl_replication = on
 spock.include_ddl_repset = on
 spock.allow_ddl_from_functions = on

---

`198-211`: **Add language specifier to ASCII diagram block.**

For consistency and to satisfy linters, add a language specifier (e.g., `text`) to the ASCII diagram code block.

```diff
-```
+```text
 PRIMARY (server-a)                          STANDBY (server-b)
 ┌─────────────────────┐                     ┌─────────────────────┐

docker/docker-compose.yml (1)> 410-419: Clarify migration ordering to avoid accidental reordering.

Line 415 maps 97-snowflake.sql96-snowflake.sql and Line 417 maps 99-spock.sql97-spock.sql, which makes ordering non-obvious and easy to break when new migrations are added. Consider aligning target filenames with source numbering or adding a brief comment explaining the intentional ordering.

Should be resolved

Should be resolved

# Conflicts:
#	docker/.env.example
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
docker/.env.example (1)

144-166: Optional: align env key ordering with dotenv-linter warnings.

Static analysis flags UnorderedKey/ExtraBlankLine here. If dotenv-linter runs in CI, consider reordering MINIO_ROOT_* and WAL_* keys (or configure the linter to ignore ordering for this example file).

♻️ Possible cleanup (optional)
- MINIO_ROOT_USER=supa-storage
- MINIO_ROOT_PASSWORD=secret1234
-
+ MINIO_ROOT_PASSWORD=secret1234
+ MINIO_ROOT_USER=supa-storage
  ############
  # Spock Bi-Directional Replication (Optional)
  ############

- WAL_LEVEL=logical
- MAX_WAL_SENDERS=10
- MAX_REPLICATION_SLOTS=10
- WAL_KEEP_SIZE=1GB
- HOT_STANDBY=on
+ HOT_STANDBY=on
+ MAX_REPLICATION_SLOTS=10
+ MAX_WAL_SENDERS=10
+ WAL_KEEP_SIZE=1GB
+ WAL_LEVEL=logical
docker/docker-compose.yml (2)

412-420: Clarify init script ordering if multiple 97- migrations exist.*

/docker-entrypoint-initdb.d runs files in lexicographic order. With other 97-* scripts (e.g., 97-_supabase.sql) mounted elsewhere, the actual execution order can be non-obvious. If order matters, consider renaming prefixes or update the comment to reflect interleaving.

✍️ Comment-only clarification
-      # Spock/Snowflake init scripts run before upstream migrations (96-98 < 99)
-      # Order: snowflake extension → spock extension → replication user
+      # Init scripts run in lexicographic order inside /docker-entrypoint-initdb.d/migrations.
+      # These 96-98 prefixed files run before 99-* migrations; if other 97-* scripts
+      # must run earlier/later (e.g., 97-_supabase.sql), consider renaming prefixes to make it explicit.

466-466: Consider requiring an explicit REPLICATION_PASSWORD.

Using a fallback placeholder can lead to accidental insecure deployments if the variable is omitted. If you want to fail fast, require an explicit value (or use secrets).

🔐 Example fail-fast interpolation
-      REPLICATION_PASSWORD: ${REPLICATION_PASSWORD:-spock_replication_password_change_me}
+      REPLICATION_PASSWORD: ${REPLICATION_PASSWORD:?set REPLICATION_PASSWORD in .env}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant