Skip to content

Conversation

@CHOIJEWON
Copy link

@CHOIJEWON CHOIJEWON commented Dec 16, 2025

Summary

Fixes #28896

This PR fixes issue #28896 where $connect() succeeds even when PostgreSQL is stopped when using @prisma/adapter-pg. The root cause was that pg.Pool uses lazy connection initialization, so creating a pool doesn't actually establish a database connection until the first query is executed.

Problem

Before this fix:

const adapter = new PrismaPgAdapterFactory(config)
await prisma.$connect()  // ✅ Succeeds even when PostgreSQL is offline
await prisma.user.findMany()  // ❌ Fails here with connection error

Expected behavior:

const adapter = new PrismaPgAdapterFactory(config)
await prisma.$connect()  // ❌ Should fail immediately if database is unreachable

Root Cause

The PrismaPgAdapterFactory.connect() method only instantiated new pg.Pool(config) without validating the connection. Since pg.Pool uses lazy connection initialization, no actual connection attempt occurred until the first query execution, causing $connect() to succeed even when the database was offline.

Solution

Added a validateConnection() private method that executes SELECT 1 during the connect() phase to validate the database is reachable:

private async validateConnection(pool: pg.Pool): Promise<void> {
  const tag = '[js::validateConnection]'

  try {
    // SELECT 1 is the lightest query (best for connection validation)
    await pool.query('SELECT 1')
    debug(`${tag} Connection validated successfully`)
  } catch (e) {
    debug(`${tag} Connection failed: %O`, e)
    throw e
  }
}

This method is called in connect() before returning the adapter, ensuring that any connection errors are surfaced immediately during $connect() rather than being deferred to the first query.

Changes Made

packages/adapter-pg/src/pg.ts:

  • Added validateConnection() private method (lines 318-329)
  • Call validateConnection() in connect() before returning adapter (line 284)

packages/adapter-pg/src/__tests__/pg.test.ts:

Testing

Regression Tests:

it('should throw an error when database is unreachable during connect', async () => {
  const config: pg.PoolConfig = {
    user: 'test',
    password: 'test',
    database: 'test',
    host: '192.0.2.1', // TEST-NET-1 - reserved for documentation, guaranteed to be unreachable
    port: 54321,
    connectionTimeoutMillis: 100,
  }
  const factory = new PrismaPgAdapterFactory(config)

  await expect(factory.connect()).rejects.toThrow()
})

it('should throw an error when database connection fails with external pool', async () => {
  const mockPool = new pg.Pool({...})
  mockPool.query = vi.fn().mockRejectedValue(new Error('Connection refused'))

  const factory = new PrismaPgAdapterFactory(mockPool)
  await expect(factory.connect()).rejects.toThrow('Connection refused')

  await mockPool.end()
})

Test Requirements:

  • Tests require Docker PostgreSQL to be running (postgres://prisma:prisma@localhost:5432/tests)
  • Matches Prisma's CI/CD testing pattern
  • Uses real database connections instead of mocks for integration testing

Behavior Changes

After this fix:

const adapter = new PrismaPgAdapterFactory(config)
await prisma.$connect()  // ❌ Now fails immediately if PostgreSQL is unreachable

This is the correct behavior and matches user expectations for connection validation.

Related Work

  • Similar validation pattern exists in @prisma/adapter-mariadb which uses SELECT VERSION() for capability detection
  • @prisma/adapter-neon has similar connection error handling tests

Breaking Changes

None. This is a bug fix that makes the behavior match documented expectations.


Additional Notes

The ECONNREFUSED error when Docker PostgreSQL is not running is expected and acceptable behavior - it confirms that the connection validation is working correctly and surfacing errors immediately during $connect() as intended.

Summary by CodeRabbit

  • New Features

    • Added stronger connection validation and error handling to fail fast when the database or connection pool is unreachable.
  • Tests

    • Consolidated test configuration and added cases verifying failures for unreachable databases and simulated pool connection errors.

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

@CLAassistant
Copy link

CLAassistant commented Dec 16, 2025

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

Walkthrough

Adds a connection validation step to PrismaPgAdapterFactory by running SELECT 1 during connect(). Refactors tests to use a shared testConfig derived from TEST_POSTGRES_URI and adds two tests for connection-failure scenarios. A duplicate validateConnection declaration appears in the diff.

Changes

Cohort / File(s) Summary
Tests: shared config & failure cases
packages/adapter-pg/src/__tests__/pg.test.ts
Adds getTestConfig() helper to derive pg.PoolConfig from TEST_POSTGRES_URI; replaces hardcoded pool configs with testConfig; adds tests verifying connect() throws when DB is unreachable and when an external Pool fails to connect (mocked).
Connection validation in adapter
packages/adapter-pg/src/pg.ts
PrismaPgAdapterFactory.connect() now awaits a new private validateConnection(pool: pg.Pool) which runs SELECT 1 to verify connectivity and throws DriverAdapterError(convertDriverError(e)) on failure. The same validateConnection method is declared twice in the diff.

Suggested labels

lgtm

Suggested reviewers

  • jacek-prisma
  • aqrln
  • jkomyno

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding connection validation during the connect() method to ensure database connectivity.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ae866e and af5b0d4.

📒 Files selected for processing (1)
  • packages/adapter-pg/src/pg.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for new file names (e.g., query-utils.ts, filter-operators.test.ts)
Avoid creating barrel files (index.ts that re-export from other modules). Import directly from the source file (e.g., import { foo } from './utils/query-utils' not import { foo } from './utils'), unless ./utils/index.ts file already exists

Files:

  • packages/adapter-pg/src/pg.ts
🧬 Code graph analysis (1)
packages/adapter-pg/src/pg.ts (2)
packages/driver-adapter-utils/src/error.ts (1)
  • DriverAdapterError (3-11)
packages/adapter-pg/src/errors.ts (1)
  • convertDriverError (37-58)
🔇 Additional comments (3)
packages/adapter-pg/src/pg.ts (3)

284-291: LGTM! Resource cleanup properly implemented.

The try/catch block correctly addresses the resource leak concern from the previous review. The pool is only cleaned up when it was created internally (!this.externalPool), and the original error is properly propagated.


325-336: LGTM! Connection validation correctly implemented.

The validation logic properly executes SELECT 1 to verify database connectivity and wraps errors using DriverAdapterError(convertDriverError(e)), which aligns with the error handling pattern used in performIO() (line 144). The debug logging for both success and failure cases will aid troubleshooting.


325-336: Note: No duplicate declaration found.

The AI summary mentions a duplicate validateConnection declaration, but only one declaration exists in the code at lines 325-336. The implementation is correct.


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.

Copy link
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 531886f and 1fdc311.

📒 Files selected for processing (2)
  • packages/adapter-pg/src/__tests__/pg.test.ts (6 hunks)
  • packages/adapter-pg/src/pg.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for new file names (e.g., query-utils.ts, filter-operators.test.ts)
Avoid creating barrel files (index.ts that re-export from other modules). Import directly from the source file (e.g., import { foo } from './utils/query-utils' not import { foo } from './utils'), unless ./utils/index.ts file already exists

Files:

  • packages/adapter-pg/src/__tests__/pg.test.ts
  • packages/adapter-pg/src/pg.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Test files named *.test.ts are excluded from build output via esbuild config; place tests alongside source files

Files:

  • packages/adapter-pg/src/__tests__/pg.test.ts
🔇 Additional comments (2)
packages/adapter-pg/src/pg.ts (1)

281-303: LGTM! Connection validation ensures early failure detection.

The addition of validateConnection(client) before setting up error handlers correctly addresses the lazy connection issue with pg.Pool. This ensures connection errors surface during $connect() rather than on first query.

packages/adapter-pg/src/__tests__/pg.test.ts (1)

113-127: LGTM! Good regression test for unreachable database.

Excellent use of the TEST-NET-1 reserved IP address (192.0.2.1) which is guaranteed to be unreachable, and short timeout for fast test execution. This correctly validates the fix for issue #28896.

…nd error handling

  - Add explicit type guard for TEST_POSTGRES_URI instead of type assertion
  - Use try-finally to ensure test resource cleanup
  - Wrap validation errors in DriverAdapterError for consistenc
@CHOIJEWON CHOIJEWON force-pushed the fix/prisma-postgres-connection branch from a831973 to 1ae866e Compare December 19, 2025 01:09
Copy link
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

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a831973 and 1ae866e.

📒 Files selected for processing (2)
  • packages/adapter-pg/src/__tests__/pg.test.ts (6 hunks)
  • packages/adapter-pg/src/pg.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for new file names (e.g., query-utils.ts, filter-operators.test.ts)
Avoid creating barrel files (index.ts that re-export from other modules). Import directly from the source file (e.g., import { foo } from './utils/query-utils' not import { foo } from './utils'), unless ./utils/index.ts file already exists

Files:

  • packages/adapter-pg/src/__tests__/pg.test.ts
  • packages/adapter-pg/src/pg.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Test files named *.test.ts are excluded from build output via esbuild config; place tests alongside source files

Files:

  • packages/adapter-pg/src/__tests__/pg.test.ts
🧬 Code graph analysis (2)
packages/adapter-pg/src/__tests__/pg.test.ts (1)
packages/adapter-pg/src/pg.ts (1)
  • PrismaPgAdapterFactory (262-329)
packages/adapter-pg/src/pg.ts (2)
packages/driver-adapter-utils/src/error.ts (1)
  • DriverAdapterError (3-11)
packages/adapter-pg/src/errors.ts (1)
  • convertDriverError (37-58)
🔇 Additional comments (4)
packages/adapter-pg/src/pg.ts (1)

317-328: LGTM!

The validation logic correctly executes a simple query to verify connectivity and properly wraps errors using DriverAdapterError(convertDriverError(e)), maintaining consistency with error handling elsewhere in the adapter.

packages/adapter-pg/src/__tests__/pg.test.ts (3)

7-23: LGTM!

The helper function properly validates the environment variable and provides a clear error message when missing. Centralizing test configuration in testConfig improves maintainability.


132-151: LGTM!

The test correctly simulates connection failure with an external pool and ensures cleanup via the finally block, even if the assertion fails.


116-130: Verify pool cleanup when factory.connect() fails during validation.

When PrismaPgAdapterFactory.connect() throws an error after creating a pg.Pool (e.g., during connection timeout before validation succeeds), ensure the pool is properly cleaned up. While the timeout prevents actual database connections from being established, the pool object still exists and requires pool.end() to shut down internal timers. Verify that either:

  1. The factory handles pool cleanup on connection failure, or
  2. The test teardown properly cleans up the factory instance

Orphaned pools in repeated test runs can accumulate even without actual database connections.

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.

$connect() always succeeds in Prisma v7 using @prisma/adapter-pg even when DB is unreachable

2 participants