- daily-evaluation nutzt die Jahre 2011/2012 statt 2001, damit der Job nicht die Daten von autopilot und reminders liest und verändert. - throttler: app.listen(0) einmal je App statt eines Servers je Anfrage (ECONNRESET bei 40 parallelen Anfragen). - outbox-claims: 120 s Zeitlimit für 2000 sequentielle Anweisungen. - migration-harness: eine `migrate deploy` zur Zeit über eine eigene Advisory-Sperre (P1002 bei parallelen Migrations-Suites). - authority-notices und authority-notices-herd laufen über exclusive-suite nacheinander; Herd-Wiederholung räumt eigene Entwürfe. Refs #32 Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
32 lines
1.1 KiB
TypeScript
32 lines
1.1 KiB
TypeScript
import { Client } from 'pg';
|
|
|
|
/** Arbitrary keys, one per shared resource a suite needs to own alone. */
|
|
export const EXCLUSIVE_KEYS = {
|
|
/** Open drafts are unique per kind: only one suite may create/clear them. */
|
|
authorityNotices: 72707371,
|
|
} as const;
|
|
|
|
/**
|
|
* Holds a Postgres session advisory lock for the lifetime of a suite, so
|
|
* suites that clear or count a table whose rows are global (e.g. the open
|
|
* draft per notice kind) never run at the same time, even in parallel jest
|
|
* workers. Blocks until the previous owner released it; the lock disappears
|
|
* with the connection if a worker dies. Call release() in afterAll.
|
|
*/
|
|
export async function acquireExclusive(
|
|
key: number,
|
|
): Promise<() => Promise<void>> {
|
|
const url = process.env.DATABASE_URL;
|
|
if (!url) {
|
|
throw new Error('DATABASE_URL is not set');
|
|
}
|
|
const connection = new URL(url);
|
|
connection.searchParams.delete('schema');
|
|
const client = new Client({ connectionString: connection.toString() });
|
|
await client.connect();
|
|
await client.query('SELECT pg_advisory_lock($1)', [key]);
|
|
return async () => {
|
|
await client.end();
|
|
};
|
|
}
|