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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions web/lib/db.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,14 @@ describe('createPool threads idleTimeoutMillis into the pg Pool (via getPool)',
] as const;
const saved: Record<string, string | undefined> = {};

function configurePoolEnvironment(): void {
process.env.BENCH_DB_HOST = 'localhost';
process.env.BENCH_DB_NAME = 'bench';
process.env.BENCH_DB_USER = 'bench_reader';
process.env.BENCH_DB_PASSWORD = 'fixture-pw';
process.env.BENCH_DB_SSL = 'disable';
}

beforeEach(async () => {
for (const k of ENV_KEYS) saved[k] = process.env[k];
await resetPool();
Expand All @@ -246,11 +254,7 @@ describe('createPool threads idleTimeoutMillis into the pg Pool (via getPool)',
});

it('uses the resolved timeouts as pool options', () => {
process.env.BENCH_DB_HOST = 'localhost';
process.env.BENCH_DB_NAME = 'bench';
process.env.BENCH_DB_USER = 'bench_reader';
process.env.BENCH_DB_PASSWORD = 'fixture-pw'; // skips the IAM/Signer path
process.env.BENCH_DB_SSL = 'disable'; // avoids the BENCH_DB_CA requirement
configurePoolEnvironment();
process.env.BENCH_DB_IDLE_TIMEOUT_MS = '123456';
process.env.BENCH_DB_STATEMENT_TIMEOUT_MS = '23456';

Expand All @@ -262,6 +266,17 @@ describe('createPool threads idleTimeoutMillis into the pg Pool (via getPool)',
expect(pool.options.idleTimeoutMillis).toBe(123456);
expect(pool.options.statement_timeout).toBe(23456);
});

it('handles idle-client errors on the shared pool', () => {
configurePoolEnvironment();
const pool = getPool();
const error = new Error('fixture idle-client disconnect');
const log = vi.spyOn(console, 'error').mockImplementation(() => {});

expect(pool.listenerCount('error')).toBe(1);
expect(() => pool.emit('error', error)).not.toThrow();
expect(log).toHaveBeenCalledWith('bench: idle PostgreSQL client error', error);
});
});

describe('requireEnv', () => {
Expand Down
9 changes: 8 additions & 1 deletion web/lib/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export function passwordProvider(config: DbConfig): () => Promise<string> {
}

function createPool(config: DbConfig = readConfig()): Pool {
return new Pool({
const pool = new Pool({
host: config.host,
port: config.port,
database: config.database,
Expand All @@ -184,6 +184,13 @@ function createPool(config: DbConfig = readConfig()): Pool {
idleTimeoutMillis: config.idleTimeoutMillis,
statement_timeout: config.statementTimeoutMillis,
});
// pg emits idle-client connection failures on the pool. Without this
// listener, EventEmitter turns a recoverable disconnect into an uncaught
// exception. The pool removes the failed client and replaces it on demand.
pool.on('error', (error) => {
console.error('bench: idle PostgreSQL client error', error);
});
return pool;
}

// A single pool is shared across warm serverless invocations and survives
Expand Down
Loading