@steve31415/baselib 3.6.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db.d.ts +17 -0
- package/dist/db.js +25 -1
- package/package.json +1 -1
package/dist/db.d.ts
CHANGED
|
@@ -20,10 +20,27 @@ export interface PoolOptions {
|
|
|
20
20
|
* default is to wait forever, which turns pool starvation into a silent
|
|
21
21
|
* hang instead of an error). */
|
|
22
22
|
connectionTimeoutMillis?: number;
|
|
23
|
+
/** Server-side cap on any single SQL statement, sent in each connection's
|
|
24
|
+
* startup packet (default DEFAULT_STATEMENT_TIMEOUT_MS; 0 disables). */
|
|
25
|
+
statementTimeoutMillis?: number;
|
|
23
26
|
/** Receives idle-client errors (see logPoolErrors). Without one they go to
|
|
24
27
|
* stderr — on the Cloud Logging backstop, but not in Axiom. */
|
|
25
28
|
logger?: Logger;
|
|
26
29
|
}
|
|
30
|
+
/** Why a fleet-wide statement timeout (mirror, 2026-09-08): a transaction an
|
|
31
|
+
* app deliberately holds open across an application await (mirror's job
|
|
32
|
+
* claims) can sit in a lock cycle whose closing edge lives in Node —
|
|
33
|
+
* invisible to Postgres's deadlock detector, so it is never broken, and
|
|
34
|
+
* every statement queued behind it waits forever (mirror's twitter
|
|
35
|
+
* ingestion was silently down ~12 h). With the cap, blocked statements
|
|
36
|
+
* abort (SQLSTATE 57014), their transactions roll back, and the pile-up
|
|
37
|
+
* self-heals. 10 minutes is deliberately far above any healthy statement in
|
|
38
|
+
* the fleet (milliseconds to seconds): never cancel legitimate work, just
|
|
39
|
+
* bound the pathological case (Steve, 2026-09-08). A migration that
|
|
40
|
+
* legitimately needs longer (huge index build) should start with
|
|
41
|
+
* `set local statement_timeout = 0` — migrate() runs each file in its own
|
|
42
|
+
* transaction, so the override scopes to that migration. */
|
|
43
|
+
export declare const DEFAULT_STATEMENT_TIMEOUT_MS: number;
|
|
27
44
|
/** pg-pool re-emits an idle client's socket error as its own 'error' event —
|
|
28
45
|
* in production "Connection terminated unexpectedly" when Cloud SQL closes an
|
|
29
46
|
* idle connection. Unhandled, that event throws and takes the process down
|
package/dist/db.js
CHANGED
|
@@ -30,6 +30,20 @@ export async function cloudSqlClientOptions(instance, auth) {
|
|
|
30
30
|
});
|
|
31
31
|
return { options, connector };
|
|
32
32
|
}
|
|
33
|
+
/** Why a fleet-wide statement timeout (mirror, 2026-09-08): a transaction an
|
|
34
|
+
* app deliberately holds open across an application await (mirror's job
|
|
35
|
+
* claims) can sit in a lock cycle whose closing edge lives in Node —
|
|
36
|
+
* invisible to Postgres's deadlock detector, so it is never broken, and
|
|
37
|
+
* every statement queued behind it waits forever (mirror's twitter
|
|
38
|
+
* ingestion was silently down ~12 h). With the cap, blocked statements
|
|
39
|
+
* abort (SQLSTATE 57014), their transactions roll back, and the pile-up
|
|
40
|
+
* self-heals. 10 minutes is deliberately far above any healthy statement in
|
|
41
|
+
* the fleet (milliseconds to seconds): never cancel legitimate work, just
|
|
42
|
+
* bound the pathological case (Steve, 2026-09-08). A migration that
|
|
43
|
+
* legitimately needs longer (huge index build) should start with
|
|
44
|
+
* `set local statement_timeout = 0` — migrate() runs each file in its own
|
|
45
|
+
* transaction, so the override scopes to that migration. */
|
|
46
|
+
export const DEFAULT_STATEMENT_TIMEOUT_MS = 10 * 60_000;
|
|
33
47
|
/** pg-pool re-emits an idle client's socket error as its own 'error' event —
|
|
34
48
|
* in production "Connection terminated unexpectedly" when Cloud SQL closes an
|
|
35
49
|
* idle connection. Unhandled, that event throws and takes the process down
|
|
@@ -51,6 +65,10 @@ export async function createPool(opts = {}) {
|
|
|
51
65
|
return pool;
|
|
52
66
|
}
|
|
53
67
|
async function newPool(opts) {
|
|
68
|
+
// pg sends statement_timeout in the startup packet of every connection —
|
|
69
|
+
// server-enforced, no per-connection SET. A 0 is falsy, so pg omits it and
|
|
70
|
+
// the server default (no timeout) applies.
|
|
71
|
+
const statement_timeout = opts.statementTimeoutMillis ?? DEFAULT_STATEMENT_TIMEOUT_MS;
|
|
54
72
|
const instance = process.env.CLOUD_SQL_INSTANCE;
|
|
55
73
|
if (instance) {
|
|
56
74
|
const { options: clientOpts } = await cloudSqlClientOptions(instance);
|
|
@@ -60,12 +78,18 @@ async function newPool(opts) {
|
|
|
60
78
|
database: opts.database ?? requireEnv('DB_NAME'),
|
|
61
79
|
max: opts.max ?? 3, // shared instance: default max_connections is low
|
|
62
80
|
connectionTimeoutMillis: opts.connectionTimeoutMillis,
|
|
81
|
+
statement_timeout,
|
|
63
82
|
});
|
|
64
83
|
}
|
|
65
84
|
const url = new URL(requireEnv('DATABASE_URL'));
|
|
66
85
|
if (opts.database)
|
|
67
86
|
url.pathname = `/${opts.database}`;
|
|
68
|
-
return new pg.Pool({
|
|
87
|
+
return new pg.Pool({
|
|
88
|
+
connectionString: url.toString(),
|
|
89
|
+
max: opts.max ?? 5,
|
|
90
|
+
connectionTimeoutMillis: opts.connectionTimeoutMillis,
|
|
91
|
+
statement_timeout,
|
|
92
|
+
});
|
|
69
93
|
}
|
|
70
94
|
/** End a pool and resolve only once every client's connection has actually
|
|
71
95
|
* closed. pg-pool's own `end()` resolves as soon as it has *asked* its idle
|
package/package.json
CHANGED