Skip to content

Support async replicas#1097

Merged
N2D4 merged 11 commits intodevfrom
async-replica
Jan 12, 2026
Merged

Support async replicas#1097
N2D4 merged 11 commits intodevfrom
async-replica

Conversation

@N2D4
Copy link
Copy Markdown
Contributor

@N2D4 N2D4 commented Jan 11, 2026


Note

Introduces read-replica support and ensures read-after-write consistency.

  • Backend/Prisma: Adds replication-wait extension using WAL LSN (STACK_DATABASE_REPLICATION_WAIT_STRATEGY with pg-stat-replication/aurora), validates LSN, and waits post-operations; integrates with @prisma/extension-read-replicas and routes read-only queries to $replica.
  • Dev infra: Provisions a PostgreSQL replica with configurable lag (db-replica) and ports; updates env (STACK_DATABASE_REPLICA_CONNECTION_STRING), Dockerfiles/compose, volumes, and readiness script to wait for both primary and replica; adds PgHero instance for replica; Launchpad lists replica services.
  • UI: Shows "Dev Stats" link on the backend home page in development.
  • Tests: Stabilizes email tests by awaiting inbox delivery and improves failure logs (nicify).

Written by Cursor Bugbot for commit 9a7601e. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • Added PostgreSQL read-replica support for development, a PgHero replica entry, and a dev-only "Dev Stats" link.
  • Chores

    • Provisioned replica container and startup/config scripts, added replication-related DB settings, and integrated a replication-wait strategy into DB access.
    • Updated local readiness checks to wait for both primary and replica.
  • Tests

    • Improved test logging with labeled, nicified output and synchronized email delivery waits.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link
Copy Markdown

vercel Bot commented Jan 11, 2026

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

Project Deployment Review Updated (UTC)
stack-backend Ready Ready Preview, Comment Jan 12, 2026 11:11pm
stack-dashboard Ready Ready Preview, Comment Jan 12, 2026 11:11pm
stack-demo Ready Ready Preview, Comment Jan 12, 2026 11:11pm
stack-docs Ready Ready Preview, Comment Jan 12, 2026 11:11pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 11, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds a PostgreSQL read-replica development setup, a replication-wait mechanism in the Prisma client that waits on WAL LSNs, Docker compose/image/entrypoint for the replica, UI entries and readiness checks for new ports, and a dev-only frontend link.

Changes

Cohort / File(s) Summary
Env & Readiness
apps/backend/.env.development, package.json
Switch replica connection to postgres on port 34; add STACK_DATABASE_REPLICATION_WAIT_STRATEGY; make readiness script check both ports (28 and 34).
Prisma Client & Replication Logic
apps/backend/src/prisma-client.tsx
Add replication-wait helpers (waitForReplication, extendWithReplicationWait), validate internal params for raw queries, fetch/record WAL LSNs after writes/transactions, add error handling/retries, and compose replication-wait with read-replica extension. (High logic density — review replication polling, strategy selection, LSN validation, and transaction hooks.)
Frontend Dev UI
apps/backend/src/app/page.tsx
Conditionally render a Dev Stats link when getNodeEnvironment() === "development".
Dev Launchpad UI
apps/dev-launchpad/public/index.html
Add PostgreSQL Replica background service (port suffix 34) and PgHero (Replica) app entry (port suffix 35).
Docker Compose & Volumes
docker/dependencies/docker.compose.yaml
Add db-replica, pghero-replica services and postgres-replica-data volume; configure replication env, ports, and service dependencies.
Replica Image & Entrypoint
docker/dev-postgres-replica/Dockerfile, docker/dev-postgres-replica/entrypoint.sh
New replica image and entrypoint that waits for primary, runs pg_basebackup if PGDATA empty, writes recovery config (primary_conninfo, recovery_min_apply_delay, hot_standby), and starts PostgreSQL. (Review startup/wait logic and credentials handling.)
Primary DB Replication Setup
docker/dev-postgres-with-extensions/Dockerfile
Create replication user and post-init pg_hba update; enable replication settings (wal_level=replica, max_wal_senders, wal_keep_size, hot_standby).
E2E Test logging & flows
apps/e2e/tests/backend/.../email-queue.test.ts
Use nicify(...) for logs, create mailboxes programmatically, wait for all emails to be sent before asserting outbox pagination, and replace raw logging with labeled nicified logs.

Sequence Diagram(s)

sequenceDiagram
    participant App as Backend App
    participant Prisma as Prisma Client (with replication-wait)
    participant Primary as Primary DB
    participant Replica as Read Replica

    App->>Prisma: Perform write (transaction)
    Prisma->>Primary: Execute write
    Primary-->>Prisma: Confirm + WAL LSN
    Prisma->>Prisma: Record required LSN

    App->>Prisma: Read request (may target replica)
    Prisma->>Prisma: Check STACK_DATABASE_REPLICATION_WAIT_STRATEGY
    Prisma->>Replica: Poll replica LSN/status
    Replica-->>Prisma: Current LSN

    alt Replica LSN < required LSN
        Prisma->>Replica: Poll/wait until LSN >= required
    end

    Prisma->>Replica: Execute read query
    Replica-->>Prisma: Return results
    Prisma-->>App: Return consistent data
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I tunneled to WAL and chased each tiny LSN,

waited on replicas till the numbers were seen.
Basebackup hummed, entrypoint sang at dawn,
dev panels now show the replica’s song.
Carrots and bytes—replicated, one by one.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Support async replicas' directly corresponds to the main change: adding PostgreSQL read-replica support with replication-aware reads in the backend.
Description check ✅ Passed The PR description provides comprehensive detail on all major changes across files, including Docker setup, environment variables, Prisma client extensions, and development UX improvements.

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

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e9f637 and 9a7601e.

📒 Files selected for processing (1)
  • apps/backend/src/prisma-client.tsx

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@cmux-agent
Copy link
Copy Markdown

cmux-agent Bot commented Jan 11, 2026

Older cmux preview screenshots (latest comment is below)

Preview Screenshots

Open Workspace (1 hr expiry) · Open Dev Browser (1 hr expiry) · Open Diff Heatmap

Captured 1 screenshot for commit 5540128 (2026-01-11 21:25:57.645 UTC).

Dev Launchpad page showing the new 'PostgreSQL Replica (100ms lag)' entry at port 8134 in the Background services section - this is the UI change from the async-replica PR that adds documentation for the new database replica service

dev-launchpad-background-services-async-replica.png


Generated by cmux preview system

@cmux-agent
Copy link
Copy Markdown

cmux-agent Bot commented Jan 12, 2026

Older cmux preview screenshots (latest comment is below)

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

1 similar comment
@cmux-agent
Copy link
Copy Markdown

cmux-agent Bot commented Jan 12, 2026

Older cmux preview screenshots (latest comment is below)

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

@N2D4 N2D4 marked this pull request as ready for review January 12, 2026 01:37
Copilot AI review requested due to automatic review settings January 12, 2026 01:37
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Jan 12, 2026

Claude encountered an error —— View job


I'll analyze this and get back to you.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jan 12, 2026

Greptile Overview

Greptile Summary

This PR introduces first-class PostgreSQL read replica support with read-after-write consistency for Stack Auth's backend infrastructure.

Key Changes

Database Replication Infrastructure:

  • Adds a new db-replica Docker service with configurable replication lag (default 15ms) and streaming replication from the primary database
  • Configures the primary database with wal_level=replica, max_wal_senders=3, and creates a replicator user for replication
  • Includes PgHero monitoring for both primary and replica databases

Prisma Client Extensions:

  • Implements extendWithReplicationWait() that intercepts all write operations and waits for replicas to catch up using WAL LSN tracking
  • Integrates @prisma/extension-read-replicas to route read-only queries to the replica via $replica()
  • Supports multiple replication wait strategies: none (default), pg-stat-replication (for dev/streaming), and aurora (for AWS Aurora)
  • Adds intelligent query routing - read-only queries go to the replica, writes go to the primary

Developer Experience:

  • New environment variables: STACK_DATABASE_REPLICA_CONNECTION_STRING and STACK_DATABASE_REPLICATION_WAIT_STRATEGY
  • Updates dev scripts to wait for both primary and replica readiness
  • Adds replica services to the developer launchpad UI
  • Includes Dev Stats link in development mode

Critical Issues Found

Infinite Loop Without Timeout (apps/backend/src/prisma-client.tsx:188-198):
The replication wait loop has no maximum timeout or iteration count. If a replica never catches up (disconnected, severely lagged, or misconfigured), the loop will run indefinitely inside the database, blocking connections and potentially exhausting the connection pool.

SQL Injection Risk (apps/backend/src/prisma-client.tsx:189):
The minLsnSubquery is directly interpolated into SQL using template literals within $queryRawUnsafe. While currently derived from a controlled environment variable lookup, this violates SQL injection prevention best practices and could be exploited if an attacker gains control over the environment configuration.

Unescaped Variables in Config (docker/dev-postgres-replica/entrypoint.sh:42):
Environment variables are directly interpolated into PostgreSQL's primary_conninfo configuration without escaping. Special characters (particularly single quotes) in these variables could break the configuration syntax.

Architecture Considerations

The implementation correctly:

  • Only applies replication wait to the global primary client, not to Neon or standalone Postgres clients
  • Skips replication wait for operations inside transactions (detected via __internalParams)
  • Falls back to a 50ms wait if replication checking fails, though this could lead to consistency issues
  • Uses OrbStack optimization for faster local development on macOS

Confidence Score: 2/5

  • This PR has critical issues that could cause production outages and should not be merged without fixes
  • The PR introduces important database replication functionality, but contains three critical issues: (1) an infinite loop without timeout that can hang the application indefinitely if replicas lag, (2) SQL injection vulnerability through direct string interpolation of environment-derived SQL subqueries, and (3) unescaped variable interpolation in shell scripts. The infinite loop issue is particularly severe as it could exhaust database connections and cause cascading failures in production. While the overall architecture is sound and the Docker setup is well-designed, these critical bugs must be fixed before merging.
  • apps/backend/src/prisma-client.tsx requires immediate attention to fix the infinite loop and SQL injection issues. docker/dev-postgres-replica/entrypoint.sh needs variable escaping fixes.

Important Files Changed

File Analysis

Filename Score Overview
apps/backend/src/prisma-client.tsx 2/5 Adds read replica support and replication wait logic; contains critical issues with infinite loop (no timeout) and SQL injection risk in minLsnSubquery interpolation
docker/dev-postgres-replica/entrypoint.sh 3/5 Creates replica bootstrap script; has unescaped variable interpolation in PostgreSQL config that could break with special characters
docker/dependencies/docker.compose.yaml 5/5 Adds db-replica service with configurable lag, PgHero replica monitoring, proper volume management and dependencies
apps/backend/.env.development 5/5 Adds replica connection string (port 8134) and replication wait strategy configuration for development

Sequence Diagram

sequenceDiagram
    participant App as Application
    participant Primary as Primary Client
    participant PrimaryDB as Primary Database
    participant Replica as Replica Database
    participant ReplicaClient as Replica Client

    Note over App,ReplicaClient: Write Operation Flow

    App->>Primary: Execute write operation (e.g., create, update)
    Primary->>PrimaryDB: Perform write
    PrimaryDB-->>Primary: Write complete
    Primary->>PrimaryDB: SELECT pg_current_wal_lsn()
    PrimaryDB-->>Primary: Return LSN (e.g., "0/1234ABC")
    
    Note over Primary,PrimaryDB: Wait for replication (if strategy != "none")
    
    Primary->>PrimaryDB: DO $$ LOOP checking pg_stat_replication
    loop Until replica catches up or times out
        PrimaryDB->>PrimaryDB: SELECT MIN(replay_lsn) FROM pg_stat_replication
        PrimaryDB->>PrimaryDB: Compare with target LSN
        alt Replica caught up
            PrimaryDB->>PrimaryDB: EXIT loop
        else Still lagging
            PrimaryDB->>PrimaryDB: pg_sleep(0.01)
        end
    end
    PrimaryDB-->>Primary: Replication confirmed
    Primary-->>App: Write operation complete

    Note over App,ReplicaClient: Read Operation Flow

    App->>Primary: Execute read-only query
    Primary->>Primary: Check if query is read-only
    alt All queries are read-only
        Primary->>ReplicaClient: Route to $replica()
        ReplicaClient->>Replica: Execute SELECT query
        Replica-->>ReplicaClient: Return results
        ReplicaClient-->>Primary: Return results
    else Contains write operations
        Primary->>PrimaryDB: Execute on primary
        PrimaryDB-->>Primary: Return results
    end
    Primary-->>App: Return query results

    Note over PrimaryDB,Replica: Background Replication
    PrimaryDB->>Replica: Stream WAL changes (continuous)
    Replica->>Replica: Apply with recovery_min_apply_delay (15ms)
Loading

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment thread apps/backend/src/prisma-client.tsx
Comment thread apps/backend/src/prisma-client.tsx
Comment thread docker/dev-postgres-replica/entrypoint.sh
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: 3

🤖 Fix all issues with AI agents
In @apps/backend/.env.development:
- Around line 30-31: The replica connection string in
STACK_DATABASE_REPLICA_CONNECTION_STRING uses the superuser "postgres" and its
placeholder password; update that value to use the read-only database user by
replacing the username "postgres" with "readonly" and the password placeholder
"PASSWORD-PLACEHOLDER--uqfEC1hmmv" with
"PASSWORD-PLACEHOLDER--readonlyuqfEC1hmmv" so the replica connection uses the
SELECT-only user created in the Dockerfile (used by
@prisma/extension-read-replicas).

In @apps/backend/src/prisma-client.tsx:
- Around line 181-201: The DO block currently uses an infinite LOOP that can
block forever; modify the PL/pgSQL to enforce a max wait by introducing a
timeout check (e.g., capture a start timestamp with clock_timestamp() or use an
iteration counter) and compare elapsed time each loop iteration, exiting the
loop with a warning or raising an error if the configured max wait (passable via
a new parameter or constant) is exceeded; update the call site that uses
(primary as any).$queryRawUnsafe and the minLsnSubquery/lsn variables to supply
the timeout value or handle the returned timeout state so callers can respond
appropriately.
- Around line 241-261: The $allOperations hook currently calls
readLsnAndWaitForReplication(client) after every operation, adding latency for
read-only operations; update the hook in query.$allOperations to only call
readLsnAndWaitForReplication for write operations by checking the operation
string (e.g., allowlist: create, update, delete, upsert, createMany, updateMany,
deleteMany) and skipping the wait for read operations (findUnique, findMany,
findFirst, count, etc.); preserve the existing transaction short-circuit
(internalParams.transaction) and ensure the check occurs after obtaining result
but before awaiting replication.
🧹 Nitpick comments (1)
docker/dev-postgres-replica/entrypoint.sh (1)

21-23: Quote the PGDATA variable to prevent word splitting.

The ${PGDATA} variable should be quoted to handle paths with spaces and follow shell scripting best practices.

Proposed fix
-if [ -z "$(ls -A ${PGDATA} 2>/dev/null)" ]; then
+if [ -z "$(ls -A "${PGDATA}" 2>/dev/null)" ]; then
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1618f89 and cc4d3c9.

📒 Files selected for processing (9)
  • apps/backend/.env.development
  • apps/backend/src/app/page.tsx
  • apps/backend/src/prisma-client.tsx
  • apps/dev-launchpad/public/index.html
  • docker/dependencies/docker.compose.yaml
  • docker/dev-postgres-replica/Dockerfile
  • docker/dev-postgres-replica/entrypoint.sh
  • docker/dev-postgres-with-extensions/Dockerfile
  • package.json
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

For blocking alerts and errors, never use toast; instead, use alerts as toasts are easily missed by the user

Files:

  • apps/backend/src/app/page.tsx
  • apps/backend/src/prisma-client.tsx
**/*.{tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{tsx,css}: Keep hover/click animations snappy and fast; don't delay actions with pre-transitions (e.g., no fade-in on button hover) as it makes UI feel sluggish; instead apply transitions after the action like smooth fade-out when hover ends
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g., transition-colors hover:transition-none)

Files:

  • apps/backend/src/app/page.tsx
  • apps/backend/src/prisma-client.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

NEVER use Next.js dynamic functions if avoidable; prefer using client components instead to keep pages static (e.g., use usePathname instead of await params)

Files:

  • apps/backend/src/app/page.tsx
  • apps/backend/src/prisma-client.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: NEVER try-catch-all, NEVER void a promise, and NEVER use .catch(console.error) or similar; use loading indicators instead; if asynchronous handling is necessary, use runAsynchronously or runAsynchronouslyWithAlert instead
Use ES6 maps instead of records wherever possible

Files:

  • apps/backend/src/app/page.tsx
  • apps/backend/src/prisma-client.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Code defensively; prefer ?? throwErr(...) over non-null assertions with good error messages explicitly stating violated assumptions
Avoid the any type; when necessary, leave a comment explaining why it's used, why the type system fails, and how errors would be caught at compile-, test-, or runtime

Files:

  • apps/backend/src/app/page.tsx
  • apps/backend/src/prisma-client.tsx
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-07T00:55:19.871Z
Learning: The project uses PostgreSQL with Prisma ORM for database management; database models are located in `/apps/backend/src`
📚 Learning: 2026-01-07T00:55:19.871Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-07T00:55:19.871Z
Learning: Applies to .env*,**/.env* : Any environment variables created should be prefixed with `STACK_` (or `NEXT_PUBLIC_STACK_` if public) to ensure Turborepo picks up changes and improves readability

Applied to files:

  • apps/backend/.env.development
  • package.json
📚 Learning: 2026-01-07T00:55:19.871Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-07T00:55:19.871Z
Learning: Applies to **/config/schema.ts,**/config/**/*.{ts,tsx} : Whenever making backwards-incompatible changes to the config schema, update the migration functions in `packages/stack-shared/src/config/schema.ts`

Applied to files:

  • apps/backend/src/prisma-client.tsx
🧬 Code graph analysis (1)
apps/backend/src/app/page.tsx (1)
packages/stack-shared/src/utils/env.tsx (1)
  • getNodeEnvironment (76-78)
🪛 dotenv-linter (4.0.0)
apps/backend/.env.development

[warning] 30-30: [SubstitutionKey] The STACK_DATABASE_REPLICA_CONNECTION_STRING key is not assigned properly

(SubstitutionKey)


[warning] 31-31: [UnorderedKey] The STACK_DATABASE_REPLICATION_WAIT_STRATEGY key should go before the STACK_DATABASE_REPLICA_CONNECTION_STRING key

(UnorderedKey)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: Agent
  • GitHub Check: Vercel Agent Review
  • GitHub Check: Cursor Bugbot
  • GitHub Check: all-good
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
  • GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
  • GitHub Check: setup-tests-with-custom-base-port
  • GitHub Check: build (22.x)
  • GitHub Check: restart-dev-and-test-with-custom-base-port
  • GitHub Check: build (22.x)
  • GitHub Check: setup-tests
🔇 Additional comments (17)
apps/backend/src/app/page.tsx (1)

14-19: LGTM! Dev-only link is correctly conditionally rendered.

The conditional rendering based on getNodeEnvironment() appropriately restricts the "Dev Stats" link to development environments only. Since NODE_ENV is typically resolved at build time, this should not cause dynamic rendering issues in production builds.

apps/dev-launchpad/public/index.html (2)

125-125: LGTM!

The PostgreSQL Replica background service entry is correctly added with suffix 34, which aligns with the replica connection port configured in .env.development.


271-279: LGTM!

The PgHero (Replica) app entry follows the established pattern and is correctly configured with port suffix 35 and appropriate importance level.

docker/dev-postgres-with-extensions/Dockerfile (2)

31-38: LGTM! Replication user and permissions setup looks correct.

The replication user creation and pg_hba.conf configuration follow PostgreSQL streaming replication best practices. Using scram-sha-256 authentication is secure, and the "all" host specification is acceptable for development environments.


47-55: LGTM! Replication-enabled PostgreSQL configuration.

The WAL configuration settings are appropriate for streaming replication:

  • wal_level=replica enables replication-grade WAL
  • max_wal_senders=3 allows sufficient replication connections
  • wal_keep_size=64MB ensures WAL retention for replica catch-up
  • hot_standby=on enables read queries on replicas
docker/dev-postgres-replica/Dockerfile (1)

1-7: LGTM!

The Dockerfile correctly uses PostgreSQL 15 and properly delegates replica initialization to the entrypoint script. The entrypoint.sh implements solid replica bootstrap logic: it waits for the primary to be ready, performs a base backup using pg_basebackup, configures recovery settings with apply delay, and sets standby mode—all following PostgreSQL replication best practices.

package.json (1)

27-27: LGTM!

The readiness check now correctly waits for both the primary database (port 28) and the new replica (port 34) to be available before proceeding, aligning with the async replica infrastructure.

docker/dependencies/docker.compose.yaml (3)

22-38: LGTM!

The replica service is well-configured with proper dependency ordering, replication credentials, and a configurable apply delay for testing replication lag scenarios.


49-58: LGTM!

The PgHero replica service mirrors the primary setup and enables monitoring of the replica instance.


256-256: LGTM!

Volume correctly added for replica data persistence.

docker/dev-postgres-replica/entrypoint.sh (3)

1-19: LGTM!

The script initialization is well-structured with proper error handling (set -e), sensible defaults, and a reliable wait loop for primary readiness.


24-57: LGTM!

The base backup configuration is well-implemented with proper streaming replication settings, recovery configuration, and correct file permissions for the PostgreSQL data directory.


59-60: LGTM!

Using exec gosu is the correct pattern for dropping privileges and ensuring proper signal handling in Docker containers.

apps/backend/src/prisma-client.tsx (4)

7-15: LGTM!

The new imports support defensive coding practices with proper error handling and schema validation for the replication functionality.


214-230: LGTM!

The error handling is well-designed - it captures the error for visibility, logs a meaningful message, and falls back to a reasonable delay rather than failing the request or silently continuing.


264-274: LGTM!

The extension ordering is correct - applying replication wait before read replicas ensures consistency guarantees are in place before reads are routed to replicas.


176-179: LGTM!

Good defensive validation of the LSN format before using it in raw SQL, preventing potential injection even though the value originates from PostgreSQL.

Comment thread apps/backend/.env.development
Comment thread apps/backend/src/prisma-client.tsx
Comment thread apps/backend/src/prisma-client.tsx
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request adds support for asynchronous PostgreSQL replicas with configurable replication lag for development environments. It implements read-after-write consistency by waiting for replicas to catch up after write operations.

Changes:

  • Added Docker configuration for a PostgreSQL replica with streaming replication and configurable lag
  • Implemented replication wait logic in Prisma client to ensure read-after-write consistency
  • Updated development environment configuration to use the replica database

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
package.json Updated wait script to check both primary and replica database readiness
docker/dev-postgres-with-extensions/Dockerfile Enabled WAL replication settings and added replicator user for streaming replication
docker/dev-postgres-replica/Dockerfile Created new Dockerfile for replica container
docker/dev-postgres-replica/entrypoint.sh Implemented replica initialization script with pg_basebackup and delay configuration
docker/dependencies/docker.compose.yaml Added db-replica service and PgHero monitoring for replica
apps/dev-launchpad/public/index.html Added UI entries for replica database and monitoring tools
apps/backend/src/prisma-client.tsx Implemented replication wait logic and integrated with Prisma client extensions
apps/backend/src/app/page.tsx Added development-only link to dev stats page
apps/backend/.env.development Configured replica connection string and replication wait strategy

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread package.json
Comment thread docker/dev-postgres-with-extensions/Dockerfile
Comment thread docker/dependencies/docker.compose.yaml
Comment thread docker/dev-postgres-with-extensions/Dockerfile
Comment thread docker/dependencies/docker.compose.yaml
Comment thread apps/backend/src/prisma-client.tsx
Comment thread apps/backend/src/prisma-client.tsx
Comment thread docker/dev-postgres-replica/entrypoint.sh
Comment thread apps/backend/src/prisma-client.tsx
Comment thread apps/backend/src/prisma-client.tsx
Comment thread apps/backend/src/prisma-client.tsx
Comment thread apps/backend/src/prisma-client.tsx
Comment thread apps/backend/src/prisma-client.tsx
@cmux-agent
Copy link
Copy Markdown

cmux-agent Bot commented Jan 12, 2026

Older cmux preview screenshots (latest comment is below)

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

1 similar comment
@cmux-agent
Copy link
Copy Markdown

cmux-agent Bot commented Jan 12, 2026

Older cmux preview screenshots (latest comment is below)

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

@cmux-agent
Copy link
Copy Markdown

cmux-agent Bot commented Jan 12, 2026

Older cmux preview screenshots (latest comment is below)

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

@cmux-agent
Copy link
Copy Markdown

cmux-agent Bot commented Jan 12, 2026

Older cmux preview screenshots (latest comment is below)

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

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: 1

🧹 Nitpick comments (2)
apps/backend/src/prisma-client.tsx (2)

183-183: Document the any cast per coding guidelines.

Per project guidelines, any types should include a comment explaining why the type system fails here.

-    await (primary as any).$queryRawUnsafe(`
+    // Cast needed: Prisma extended client types don't expose $queryRawUnsafe in their type definitions
+    await (primary as any).$queryRawUnsafe(`

222-222: Add comment for any cast.

Similar to the previous cast, document why the type system requires this workaround.

-        const [{ lsn }] = await (client as any).$queryRaw<[{ lsn: string }]>`SELECT pg_current_wal_lsn()::text AS lsn`;
+        // Cast needed: extended Prisma client types don't properly expose $queryRaw on generic PrismaClient
+        const [{ lsn }] = await (client as any).$queryRaw<[{ lsn: string }]>`SELECT pg_current_wal_lsn()::text AS lsn`;
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a39a074 and f6da04c.

📒 Files selected for processing (1)
  • apps/backend/src/prisma-client.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{tsx,ts,jsx,js}

📄 CodeRabbit inference engine (AGENTS.md)

For blocking alerts and errors, never use toast; instead, use alerts as toasts are easily missed by the user

Files:

  • apps/backend/src/prisma-client.tsx
**/*.{tsx,css}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{tsx,css}: Keep hover/click animations snappy and fast; don't delay actions with pre-transitions (e.g., no fade-in on button hover) as it makes UI feel sluggish; instead apply transitions after the action like smooth fade-out when hover ends
When creating hover transitions, avoid hover-enter transitions and use only hover-exit transitions (e.g., transition-colors hover:transition-none)

Files:

  • apps/backend/src/prisma-client.tsx
**/*.{tsx,ts}

📄 CodeRabbit inference engine (AGENTS.md)

NEVER use Next.js dynamic functions if avoidable; prefer using client components instead to keep pages static (e.g., use usePathname instead of await params)

Files:

  • apps/backend/src/prisma-client.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: NEVER try-catch-all, NEVER void a promise, and NEVER use .catch(console.error) or similar; use loading indicators instead; if asynchronous handling is necessary, use runAsynchronously or runAsynchronouslyWithAlert instead
Use ES6 maps instead of records wherever possible

Files:

  • apps/backend/src/prisma-client.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Code defensively; prefer ?? throwErr(...) over non-null assertions with good error messages explicitly stating violated assumptions
Avoid the any type; when necessary, leave a comment explaining why it's used, why the type system fails, and how errors would be caught at compile-, test-, or runtime

Files:

  • apps/backend/src/prisma-client.tsx
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-07T00:55:19.871Z
Learning: The project uses PostgreSQL with Prisma ORM for database management; database models are located in `/apps/backend/src`
📚 Learning: 2026-01-07T00:55:19.871Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-07T00:55:19.871Z
Learning: Applies to **/config/schema.ts,**/config/**/*.{ts,tsx} : Whenever making backwards-incompatible changes to the config schema, update the migration functions in `packages/stack-shared/src/config/schema.ts`

Applied to files:

  • apps/backend/src/prisma-client.tsx
📚 Learning: 2026-01-07T00:55:19.871Z
Learnt from: CR
Repo: stack-auth/stack-auth PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-07T00:55:19.871Z
Learning: Applies to **/*.{ts,tsx} : Code defensively; prefer `?? throwErr(...)` over non-null assertions with good error messages explicitly stating violated assumptions

Applied to files:

  • apps/backend/src/prisma-client.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: build (22.x)
  • GitHub Check: E2E Tests (Node 22.x, Freestyle mock)
  • GitHub Check: E2E Tests (Node 22.x, Freestyle prod)
  • GitHub Check: build (22.x)
  • GitHub Check: setup-tests-with-custom-base-port
  • GitHub Check: restart-dev-and-test-with-custom-base-port
  • GitHub Check: restart-dev-and-test
  • GitHub Check: setup-tests
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: all-good
  • GitHub Check: Vercel Agent Review
  • GitHub Check: docker
  • GitHub Check: check_prisma_migrations (22.x)
  • GitHub Check: Cursor Bugbot
🔇 Additional comments (2)
apps/backend/src/prisma-client.tsx (2)

246-254: Good defensive validation of undocumented internal params.

Validating __internalParams with yup before use is excellent defensive coding, especially since this is an undocumented Prisma property that could change. The transaction check to avoid redundant replication waits is also correct.


264-274: Extension chaining order is correct.

Applying extendWithReplicationWait before readReplicas ensures that writes complete replication before subsequent reads are routed to replicas, which is the correct ordering for read-after-write consistency.

Comment thread apps/backend/src/prisma-client.tsx
@cmux-agent
Copy link
Copy Markdown

cmux-agent Bot commented Jan 12, 2026

Older cmux preview screenshots (latest comment is below)

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

Comment thread apps/backend/src/prisma-client.tsx
Comment thread apps/backend/src/prisma-client.tsx
@cmux-agent
Copy link
Copy Markdown

cmux-agent Bot commented Jan 12, 2026

Older cmux preview screenshots (latest comment is below)

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

@cmux-agent
Copy link
Copy Markdown

cmux-agent Bot commented Jan 12, 2026

Older cmux preview screenshots (latest comment is below)

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

@cmux-agent
Copy link
Copy Markdown

cmux-agent Bot commented Jan 12, 2026

Preview Screenshots

Open Diff Heatmap

Preview screenshots are being captured...

Workspace and dev browser links will appear here once the preview environment is ready.


Generated by cmux preview system

@N2D4 N2D4 enabled auto-merge (squash) January 12, 2026 23:06
@N2D4 N2D4 disabled auto-merge January 12, 2026 23:06
@N2D4 N2D4 merged commit fbcf66f into dev Jan 12, 2026
19 of 25 checks passed
@N2D4 N2D4 deleted the async-replica branch January 12, 2026 23:07
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.

2 participants