mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
fix(db): let the maintenance job run behind a pooler that refuses startup options
This commit is contained in:
1 parent
0d368170f3
commit
32cb75c1aa
3 files changed
+56
-1
No files matched your search
@@ -195,6 +195,9 @@ in `ignore_startup_parameters`. Either list it there, or set
|
||||
`DATABASE_SESSION_OPTIONS_DISABLED=1` so the app sends no session
|
||||
settings; set the timeout and `work_mem` on the database role instead
|
||||
(`ALTER ROLE healthlog SET work_mem = '16MB'`) if you still want them.
|
||||
The measurement maintenance job (Admin, System status) then applies its own
|
||||
settings with `SET` after connecting. Its concurrent index rebuild needs a
|
||||
session, so run it through a session-mode pool or a direct connection.
|
||||
|
||||
### Overriding the per-container pool ceiling
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("@/lib/db", () => ({
|
||||
prisma: {},
|
||||
sessionOptionsDisabled: () =>
|
||||
process.env.DATABASE_SESSION_OPTIONS_DISABLED === "1",
|
||||
}));
|
||||
|
||||
import {
|
||||
MAINTENANCE_SESSION_OPTIONS,
|
||||
maintenanceSessionStatements,
|
||||
} from "@/lib/jobs/measurement-maintenance";
|
||||
|
||||
describe("maintenance session settings behind a pooler", () => {
|
||||
afterEach(() => {
|
||||
delete process.env.DATABASE_SESSION_OPTIONS_DISABLED;
|
||||
});
|
||||
|
||||
it("restates every startup option as one SET statement", () => {
|
||||
const statements = maintenanceSessionStatements();
|
||||
const optionCount = MAINTENANCE_SESSION_OPTIONS.match(/-c /g)?.length;
|
||||
expect(statements).toHaveLength(optionCount ?? 0);
|
||||
expect(statements).toEqual([
|
||||
"SET statement_timeout = '0'",
|
||||
"SET idle_in_transaction_session_timeout = '0'",
|
||||
"SET maintenance_work_mem = '128MB'",
|
||||
"SET lock_timeout = '300000'",
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -35,6 +35,7 @@
|
||||
*/
|
||||
import { Client } from "pg";
|
||||
import type { Job } from "pg-boss";
|
||||
import { sessionOptionsDisabled } from "@/lib/db";
|
||||
|
||||
import { withJobLock } from "@/lib/jobs/job-lock";
|
||||
import { jobDone, jobFailed, type JobOutcome } from "@/lib/jobs/job-outcome";
|
||||
@@ -68,6 +69,21 @@ export interface MeasurementMaintenancePayload {
|
||||
export const MAINTENANCE_SESSION_OPTIONS =
|
||||
"-c statement_timeout=0 -c idle_in_transaction_session_timeout=0 -c maintenance_work_mem=128MB -c lock_timeout=300000";
|
||||
|
||||
/**
|
||||
* The same settings as `SET` statements, for a host behind a connection
|
||||
* pooler that refuses the `options` startup parameter
|
||||
* (`DATABASE_SESSION_OPTIONS_DISABLED`). Built from the constant above, whose
|
||||
* every value is a literal, so nothing user-supplied reaches the SQL.
|
||||
*/
|
||||
export function maintenanceSessionStatements(): string[] {
|
||||
return MAINTENANCE_SESSION_OPTIONS.split(/\s*-c\s+/)
|
||||
.filter(Boolean)
|
||||
.map((pair) => {
|
||||
const [name, value] = pair.trim().split("=");
|
||||
return `SET ${name} = '${value}'`;
|
||||
});
|
||||
}
|
||||
|
||||
/** The only shape an index name may have before it is spliced into SQL. */
|
||||
const INDEX_NAME = /^[a-z0-9_]{1,63}$/;
|
||||
|
||||
@@ -310,14 +326,20 @@ export async function handleMeasurementMaintenance(
|
||||
const guarded = await withJobLock(
|
||||
MEASUREMENT_MAINTENANCE_QUEUE,
|
||||
async () => {
|
||||
const viaStatements = sessionOptionsDisabled();
|
||||
const client = new Client({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
options: MAINTENANCE_SESSION_OPTIONS,
|
||||
options: viaStatements ? undefined : MAINTENANCE_SESSION_OPTIONS,
|
||||
keepAlive: true,
|
||||
});
|
||||
client.on("error", () => {});
|
||||
await client.connect();
|
||||
try {
|
||||
if (viaStatements) {
|
||||
for (const statement of maintenanceSessionStatements()) {
|
||||
await client.query(statement);
|
||||
}
|
||||
}
|
||||
return await runMeasurementMaintenance(client, payload);
|
||||
} finally {
|
||||
await client
|
||||
|
||||
Reference in new issue
Block a user