fix(db): let a PgBouncer host turn the session startup options off

The statement timeout and, since v1.42, work_mem reach Postgres as the
libpq options startup parameter, which PgBouncer refuses unless it is in
ignore_startup_parameters. A host behind it could drop the string by
setting the timeout to 0; work_mem is now always sent, so that no longer
worked. DATABASE_SESSION_OPTIONS_DISABLED sends no session settings,
documented in the example env, the env manifest, the compose whitelist
and the scaling guide.
This commit is contained in:
Marc-André Bombeck committed 2026-10-08 22:37:57 +02:00
1 parent f950dffad4
commit 7c6510eba9
6 files changed
+54 -2

No files matched your search

+11
View File
@@ -146,6 +146,17 @@ describe("buildSessionOptions", () => {
);
});
it("sends no startup options at all when a connection pooler needs that", () => {
process.env.DATABASE_SESSION_OPTIONS_DISABLED = "true";
try {
expect(buildSessionOptions()).toBeUndefined();
process.env.DATABASE_SESSION_OPTIONS_DISABLED = "0";
expect(buildSessionOptions()).toContain("-c work_mem=");
} finally {
delete process.env.DATABASE_SESSION_OPTIONS_DISABLED;
}
});
it("takes DATABASE_WORK_MEM and refuses a value that would inject a flag", () => {
process.env.DATABASE_WORK_MEM = "64MB";
expect(buildSessionOptions()).toContain("-c work_mem=64MB");
+21 -2
View File
@@ -108,6 +108,22 @@ export function getWorkMem(): string {
return raw && WORK_MEM_SHAPE.test(raw) ? raw : DEFAULT_WORK_MEM;
}
/**
* Whether the app may send the libpq `options` startup parameter at all.
*
* PgBouncer refuses a connection that carries a startup parameter it does not
* know (`unsupported startup parameter: options`) unless the operator lists
* it in `ignore_startup_parameters`. Up to v1.41 such a host could set the
* statement timeout to 0, which dropped the whole string; v1.42 always sends
* `work_mem`, so that no longer worked. `DATABASE_SESSION_OPTIONS_DISABLED`
* (1, true or yes) sends none of the session settings; the pooler or the
* database's own defaults then apply.
*/
export function sessionOptionsDisabled(): boolean {
const raw = process.env.DATABASE_SESSION_OPTIONS_DISABLED?.trim();
return raw !== undefined && /^(1|true|yes)$/i.test(raw);
}
/**
* Build the libpq `options` startup string applying the session settings.
* Passed straight through `PrismaPg` to the underlying `pg.Pool`, which
@@ -116,9 +132,12 @@ export function getWorkMem(): string {
*
* The timeouts are left out when disabled (timeout 0); `work_mem` is always
* set. Up to v1.41 the whole string was `undefined` at timeout 0, which would
* have taken `work_mem` with it.
* have taken `work_mem` with it. `undefined` now means exactly one thing:
* the operator turned the startup options off for a connection pooler
* ({@link sessionOptionsDisabled}).
*/
export function buildSessionOptions(): string {
export function buildSessionOptions(): string | undefined {
if (sessionOptionsDisabled()) return undefined;
const timeoutMs = getStatementTimeoutMs();
const timeouts =
timeoutMs > 0