ddlforge 0.1.1 → 0.2.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.
@@ -0,0 +1,65 @@
1
+ /**
2
+ * ddlforge - Exponential backoff with full jitter for lock-timeout retry loops.
3
+ *
4
+ * Algorithm: sleep = Math.random() * Math.min(maxDelay, baseDelay * 2 ** attempt)
5
+ * This prevents lock-convoy / stampede effects by randomising each retry interval.
6
+ */
7
+ /**
8
+ * Computes a full-jitter exponential backoff interval for the given attempt index.
9
+ *
10
+ * @param attempt 0-based attempt index (first retry = 0)
11
+ * @param options Backoff configuration
12
+ * @returns Randomised sleep duration in milliseconds
13
+ */
14
+ export function computeBackoff(attempt, options = {}) {
15
+ const baseDelayMs = options.baseDelayMs ?? 250;
16
+ const maxDelayMs = options.maxDelayMs ?? 10_000;
17
+ const maxRetries = options.maxRetries ?? 5;
18
+ const ceiling = Math.min(maxDelayMs, baseDelayMs * Math.pow(2, attempt));
19
+ const sleepMs = Math.random() * ceiling;
20
+ const exceeded = attempt >= maxRetries;
21
+ return { sleepMs, attempt, exceeded };
22
+ }
23
+ /**
24
+ * Returns a promise that resolves after `ms` milliseconds.
25
+ * Optionally accepts an AbortSignal: if the signal is aborted before the
26
+ * timer fires, the promise rejects with the signal's reason.
27
+ *
28
+ * @param ms Sleep duration in milliseconds
29
+ * @param signal Optional AbortSignal for cancellation
30
+ */
31
+ export function sleep(ms, signal) {
32
+ return new Promise((resolve, reject) => {
33
+ if (signal?.aborted) {
34
+ reject(signal.reason ?? new Error('AbortError'));
35
+ return;
36
+ }
37
+ const timer = setTimeout(() => {
38
+ signal?.removeEventListener('abort', onAbort);
39
+ resolve();
40
+ }, ms);
41
+ function onAbort() {
42
+ clearTimeout(timer);
43
+ reject(this.reason ?? new Error('AbortError'));
44
+ }
45
+ signal?.addEventListener('abort', onAbort, { once: true });
46
+ });
47
+ }
48
+ /**
49
+ * Iterates over retry attempts, yielding the computed backoff for each attempt.
50
+ * Callers should `sleep(backoff.sleepMs)` after receiving each non-first result.
51
+ *
52
+ * Example:
53
+ * ```ts
54
+ * for (const backoff of retryIterator(3, opts)) {
55
+ * if (backoff.attempt > 0) await sleep(backoff.sleepMs, signal);
56
+ * // ... attempt work ...
57
+ * }
58
+ * ```
59
+ */
60
+ export function* retryIterator(maxRetries, options = {}) {
61
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
62
+ yield computeBackoff(attempt, { ...options, maxRetries });
63
+ }
64
+ }
65
+ //# sourceMappingURL=backoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backoff.js","sourceRoot":"","sources":["../../../src/runner/backoff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoBH;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe,EAAE,UAA0B,EAAE;IAC1E,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,GAAG,CAAC;IAC/C,MAAM,UAAU,GAAI,OAAO,CAAC,UAAU,IAAK,MAAM,CAAC;IAClD,MAAM,UAAU,GAAI,OAAO,CAAC,UAAU,IAAK,CAAC,CAAC;IAE7C,MAAM,OAAO,GAAI,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1E,MAAM,OAAO,GAAI,IAAI,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC;IACzC,MAAM,QAAQ,GAAG,OAAO,IAAI,UAAU,CAAC;IAEvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AACxC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,KAAK,CAAC,EAAU,EAAE,MAAoB;IACpD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACpB,MAAM,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;YACjD,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,OAAO,EAAE,CAAC;QACZ,CAAC,EAAE,EAAE,CAAC,CAAC;QAEP,SAAS,OAAO;YACd,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAE,IAAoB,CAAC,MAAM,IAAI,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC;QAClE,CAAC;QAED,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,SAAS,CAAC,CAAC,aAAa,CAC5B,UAAkB,EAClB,UAA8C,EAAE;IAEhD,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,MAAM,cAAc,CAAC,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * ddlforge - PostgreSQL DDL execution supervisor.
3
+ *
4
+ * Responsibilities
5
+ * ────────────────
6
+ * 1. Dynamically imports `pg` at runtime (graceful error if not installed).
7
+ * 2. Parses SQL migration files into individual statements (reuses the existing
8
+ * zero-dependency lexer — no duplicate parser code).
9
+ * 3. For each statement:
10
+ * a. Opens a transaction on a fresh client.
11
+ * b. Injects `SET LOCAL lock_timeout` and `SET LOCAL statement_timeout`.
12
+ * c. Executes the statement.
13
+ * d. On error 55P03 (lock_not_available) or 57014 (query_canceled):
14
+ * – Rolls back, waits for a full-jitter backoff interval, retries.
15
+ * e. On avalanche detected by the lock monitor: rolls back and aborts.
16
+ * f. On success: commits and proceeds to the next statement.
17
+ * 4. Reports live progress via an optional EventEmitter-compatible callback.
18
+ *
19
+ * Zero-dependency invariant
20
+ * ─────────────────────────
21
+ * `pg` is imported with `await import('pg')` inside an async function so it
22
+ * never appears in the static import graph. Static linting (`ddlforge check`)
23
+ * works without `pg` installed.
24
+ */
25
+ export interface ExecutorOptions {
26
+ /** Full PostgreSQL connection string or URL */
27
+ databaseUrl: string;
28
+ /** lock_timeout value (e.g. "2500ms" or "2500"). Default: "3000ms" */
29
+ lockTimeout?: string;
30
+ /** statement_timeout value (e.g. "30000ms"). Default: "30000ms" */
31
+ statementTimeout?: string;
32
+ /** Maximum number of retry attempts per statement on lock errors. Default: 5 */
33
+ maxRetries?: number;
34
+ /** If true, parse and validate statements but never execute them. Default: false */
35
+ dryRun?: boolean;
36
+ /**
37
+ * Number of blocked backends that triggers an avalanche abort.
38
+ * Default: 1 (abort as soon as even one query queues behind ours).
39
+ */
40
+ lockQueueThreshold?: number;
41
+ /** Milliseconds between lock-monitor polls. Default: 500 */
42
+ monitorPollMs?: number;
43
+ /** Progress callback invoked after each statement attempt */
44
+ onProgress?: (event: ExecutionEvent) => void;
45
+ /** AbortSignal for external cancellation */
46
+ signal?: AbortSignal;
47
+ }
48
+ export type ExecutionEventKind = 'statement-start' | 'statement-success' | 'statement-retry' | 'statement-failed' | 'avalanche-abort' | 'dry-run-statement' | 'migration-complete' | 'migration-failed';
49
+ export interface ExecutionEvent {
50
+ kind: ExecutionEventKind;
51
+ statementIndex: number;
52
+ totalStatements: number;
53
+ statementSql: string;
54
+ attempt: number;
55
+ elapsedMs: number;
56
+ error?: string;
57
+ lockTimeoutMs?: number;
58
+ retryBackoffMs?: number;
59
+ }
60
+ export interface ExecutionResult {
61
+ success: boolean;
62
+ statementsExecuted: number;
63
+ statementsTotal: number;
64
+ durationMs: number;
65
+ error?: string;
66
+ avalanche?: boolean;
67
+ }
68
+ /**
69
+ * Executes a SQL migration file against a PostgreSQL database with:
70
+ * - per-statement lock-timeout injection
71
+ * - exponential full-jitter retry on lock acquisition failures
72
+ * - background lock-queue monitoring with automatic cancellation
73
+ *
74
+ * @param sql Full text of the migration file
75
+ * @param options Execution configuration
76
+ */
77
+ export declare function executeMigration(sql: string, options: ExecutorOptions): Promise<ExecutionResult>;
78
+ /**
79
+ * Parses a SQL migration string and returns the list of individual statements
80
+ * that would be executed. Useful for tooling and dry-run previews.
81
+ */
82
+ export declare function parseMigrationStatements(sql: string): string[];
83
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../../src/runner/executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAUH,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oFAAoF;IACpF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,4CAA4C;IAC5C,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,kBAAkB,GAC1B,iBAAiB,GACjB,mBAAmB,GACnB,iBAAiB,GACjB,kBAAkB,GAClB,iBAAiB,GACjB,mBAAmB,GACnB,oBAAoB,GACpB,kBAAkB,CAAC;AAEvB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,kBAAkB,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAuGD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,eAAe,CAAC,CAmQ1B;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAE9D"}
@@ -0,0 +1,340 @@
1
+ /**
2
+ * ddlforge - PostgreSQL DDL execution supervisor.
3
+ *
4
+ * Responsibilities
5
+ * ────────────────
6
+ * 1. Dynamically imports `pg` at runtime (graceful error if not installed).
7
+ * 2. Parses SQL migration files into individual statements (reuses the existing
8
+ * zero-dependency lexer — no duplicate parser code).
9
+ * 3. For each statement:
10
+ * a. Opens a transaction on a fresh client.
11
+ * b. Injects `SET LOCAL lock_timeout` and `SET LOCAL statement_timeout`.
12
+ * c. Executes the statement.
13
+ * d. On error 55P03 (lock_not_available) or 57014 (query_canceled):
14
+ * – Rolls back, waits for a full-jitter backoff interval, retries.
15
+ * e. On avalanche detected by the lock monitor: rolls back and aborts.
16
+ * f. On success: commits and proceeds to the next statement.
17
+ * 4. Reports live progress via an optional EventEmitter-compatible callback.
18
+ *
19
+ * Zero-dependency invariant
20
+ * ─────────────────────────
21
+ * `pg` is imported with `await import('pg')` inside an async function so it
22
+ * never appears in the static import graph. Static linting (`ddlforge check`)
23
+ * works without `pg` installed.
24
+ */
25
+ import { splitStatements } from '../lexer/sqlTokenizer.js';
26
+ import { sleep, computeBackoff } from './backoff.js';
27
+ import { startLockMonitor, isLockError } from './locksMonitor.js';
28
+ /**
29
+ * Dynamically imports pg and returns the Pool and Client constructors.
30
+ * If pg is not installed, prints clear installation instructions and throws.
31
+ */
32
+ async function loadPg() {
33
+ try {
34
+ // Dynamic import: NOT in static import graph — ddlforge check still works
35
+ const mod = await import('pg');
36
+ return mod.default;
37
+ }
38
+ catch {
39
+ const msg = [
40
+ '',
41
+ ' ╔══════════════════════════════════════════════════════════╗',
42
+ ' ║ ddlforge apply requires the "pg" package to be ║',
43
+ ' ║ installed in your project. ║',
44
+ ' ║ ║',
45
+ ' ║ Run one of: ║',
46
+ ' ║ npm install pg ║',
47
+ ' ║ yarn add pg ║',
48
+ ' ║ pnpm add pg ║',
49
+ ' ╚══════════════════════════════════════════════════════════╝',
50
+ '',
51
+ ].join('\n');
52
+ process.stderr.write(msg + '\n');
53
+ throw new Error('pg package is not installed. Run: npm install pg');
54
+ }
55
+ }
56
+ /* ------------------------------------------------------------------ */
57
+ /* Helpers */
58
+ /* ------------------------------------------------------------------ */
59
+ /**
60
+ * Normalises a timeout value into a bare integer millisecond string
61
+ * suitable for `SET LOCAL lock_timeout = '<value>'`.
62
+ *
63
+ * Accepts:
64
+ * - "3000ms" → "3000"
65
+ * - "3s" → "3000" (NOT handled — pass pre-converted ms values)
66
+ * - "3000" → "3000"
67
+ * - 3000 → "3000"
68
+ */
69
+ function normaliseTimeoutMs(value) {
70
+ const s = String(value).trim();
71
+ if (s.endsWith('ms')) {
72
+ return s.slice(0, -2);
73
+ }
74
+ return s;
75
+ }
76
+ /**
77
+ * Returns the `code` from a Postgres-driver error object.
78
+ * pg errors expose `.code` as a top-level property.
79
+ */
80
+ function pgErrorCode(err) {
81
+ if (err !== null && typeof err === 'object' && 'code' in err) {
82
+ return String(err['code'] ?? '');
83
+ }
84
+ return '';
85
+ }
86
+ /* ------------------------------------------------------------------ */
87
+ /* Statement text reconstruction */
88
+ /* ------------------------------------------------------------------ */
89
+ /**
90
+ * Extracts the original SQL text for a statement from the migration file.
91
+ * We reconstruct it from the token raw values so that whitespace is
92
+ * preserved reasonably well for display purposes.
93
+ */
94
+ function statementText(raw) {
95
+ // The lexer joins tokens with single spaces; we just use the raw field.
96
+ return raw.trimEnd() + ';';
97
+ }
98
+ /* ------------------------------------------------------------------ */
99
+ /* Main executor */
100
+ /* ------------------------------------------------------------------ */
101
+ /**
102
+ * Executes a SQL migration file against a PostgreSQL database with:
103
+ * - per-statement lock-timeout injection
104
+ * - exponential full-jitter retry on lock acquisition failures
105
+ * - background lock-queue monitoring with automatic cancellation
106
+ *
107
+ * @param sql Full text of the migration file
108
+ * @param options Execution configuration
109
+ */
110
+ export async function executeMigration(sql, options) {
111
+ const { databaseUrl, lockTimeout = '3000ms', statementTimeout = '30000ms', maxRetries = 5, dryRun = false, lockQueueThreshold = 1, monitorPollMs = 500, onProgress, signal, } = options;
112
+ const lockTimeoutMs = normaliseTimeoutMs(lockTimeout);
113
+ const statementTimeoutMs = normaliseTimeoutMs(statementTimeout);
114
+ const migrationStart = Date.now();
115
+ // ── Parse statements (zero-dependency lexer) ────────────────────────
116
+ const statements = splitStatements(sql);
117
+ const total = statements.length;
118
+ if (total === 0) {
119
+ return { success: true, statementsExecuted: 0, statementsTotal: 0, durationMs: 0 };
120
+ }
121
+ // ── Dry run mode ─────────────────────────────────────────────────────
122
+ if (dryRun) {
123
+ for (let i = 0; i < statements.length; i++) {
124
+ const stmt = statements[i];
125
+ onProgress?.({
126
+ kind: 'dry-run-statement',
127
+ statementIndex: i,
128
+ totalStatements: total,
129
+ statementSql: statementText(stmt.raw),
130
+ attempt: 0,
131
+ elapsedMs: Date.now() - migrationStart,
132
+ });
133
+ }
134
+ return {
135
+ success: true,
136
+ statementsExecuted: 0,
137
+ statementsTotal: total,
138
+ durationMs: Date.now() - migrationStart,
139
+ };
140
+ }
141
+ // ── Load pg ──────────────────────────────────────────────────────────
142
+ const { Pool, Client } = await loadPg();
143
+ // One pool for the migration; monitor gets its own direct clients
144
+ const pool = new Pool({ connectionString: databaseUrl, max: 1 });
145
+ let statementsExecuted = 0;
146
+ let globalError;
147
+ let avalanche = false;
148
+ try {
149
+ for (let i = 0; i < statements.length; i++) {
150
+ if (signal?.aborted) {
151
+ globalError = 'Execution aborted by caller signal.';
152
+ break;
153
+ }
154
+ const stmt = statements[i];
155
+ const stmtSql = statementText(stmt.raw);
156
+ const stmtStart = Date.now();
157
+ let succeeded = false;
158
+ let stmtError;
159
+ onProgress?.({
160
+ kind: 'statement-start',
161
+ statementIndex: i,
162
+ totalStatements: total,
163
+ statementSql: stmtSql,
164
+ attempt: 0,
165
+ elapsedMs: stmtStart - migrationStart,
166
+ lockTimeoutMs: Number(lockTimeoutMs),
167
+ });
168
+ // ── Retry loop per statement ──────────────────────────────────────
169
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
170
+ if (signal?.aborted)
171
+ break;
172
+ // Jitter-sleep before all retries (not before first attempt)
173
+ if (attempt > 0) {
174
+ const backoff = computeBackoff(attempt - 1, {
175
+ baseDelayMs: 250,
176
+ maxDelayMs: 10_000,
177
+ maxRetries,
178
+ });
179
+ onProgress?.({
180
+ kind: 'statement-retry',
181
+ statementIndex: i,
182
+ totalStatements: total,
183
+ statementSql: stmtSql,
184
+ attempt,
185
+ elapsedMs: Date.now() - migrationStart,
186
+ retryBackoffMs: backoff.sleepMs,
187
+ });
188
+ await sleep(backoff.sleepMs, signal);
189
+ if (signal?.aborted)
190
+ break;
191
+ }
192
+ // Grab a client from the pool for this attempt
193
+ const client = await pool.connect();
194
+ // Snapshot our backend PID for the lock monitor
195
+ let migrationPid = -1;
196
+ try {
197
+ const pidResult = await client.query('SELECT pg_backend_pid() AS pid');
198
+ migrationPid = Number(pidResult.rows[0]?.['pid'] ?? -1);
199
+ }
200
+ catch {
201
+ // Fallback: proceed without monitoring
202
+ }
203
+ // Start the lock monitor on its own independent connection
204
+ const monitorClientFactory = async () => {
205
+ const monClient = new Client({ connectionString: databaseUrl });
206
+ await monClient.connect?.();
207
+ return monClient;
208
+ };
209
+ const monitor = migrationPid > 0
210
+ ? startLockMonitor({
211
+ migrationPid,
212
+ clientFactory: monitorClientFactory,
213
+ pollIntervalMs: monitorPollMs,
214
+ queueThreshold: lockQueueThreshold,
215
+ signal,
216
+ })
217
+ : null;
218
+ try {
219
+ // Open transaction
220
+ await client.query('BEGIN');
221
+ // Inject session-local timeouts (scoped to this transaction)
222
+ await client.query(`SET LOCAL lock_timeout = '${lockTimeoutMs}'`);
223
+ await client.query(`SET LOCAL statement_timeout = '${statementTimeoutMs}'`);
224
+ // Execute the DDL statement
225
+ await client.query(stmtSql);
226
+ // Commit
227
+ await client.query('COMMIT');
228
+ // Stop monitor — execution succeeded, no avalanche
229
+ monitor?.stop();
230
+ const monResult = await monitor?.result;
231
+ if (monResult?.avalanche) {
232
+ // Race condition: monitor fired just as we committed.
233
+ // The COMMIT succeeded so we treat this as a success.
234
+ // (pg_cancel_backend after COMMIT is a no-op.)
235
+ }
236
+ succeeded = true;
237
+ stmtError = undefined;
238
+ break; // exit retry loop
239
+ }
240
+ catch (err) {
241
+ const code = pgErrorCode(err);
242
+ // Stop monitor before doing anything else
243
+ monitor?.stop();
244
+ const monResult = await monitor?.result;
245
+ // Rollback the failed transaction
246
+ try {
247
+ await client.query('ROLLBACK');
248
+ }
249
+ catch { /* ignore */ }
250
+ if (monResult?.avalanche) {
251
+ // Lock-queue avalanche: abort the entire migration
252
+ onProgress?.({
253
+ kind: 'avalanche-abort',
254
+ statementIndex: i,
255
+ totalStatements: total,
256
+ statementSql: stmtSql,
257
+ attempt,
258
+ elapsedMs: Date.now() - migrationStart,
259
+ error: `Lock-queue avalanche: ${monResult.blockedCount} backend(s) queued. Cancelled at ${monResult.cancelledAt}.`,
260
+ });
261
+ globalError = `Lock-queue avalanche detected. ${monResult.blockedCount} backend(s) were waiting behind migration PID ${migrationPid}. Migration aborted to protect connection pool.`;
262
+ avalanche = true;
263
+ break;
264
+ }
265
+ if (isLockError(code) && attempt < maxRetries) {
266
+ // Retryable lock error — continue retry loop
267
+ stmtError = `[${code}] ${err.message ?? String(err)}`;
268
+ continue;
269
+ }
270
+ // Non-retryable error or max retries exhausted
271
+ stmtError = `[${code || 'ERR'}] ${err.message ?? String(err)}`;
272
+ break;
273
+ }
274
+ finally {
275
+ client.release?.();
276
+ }
277
+ } // end retry loop
278
+ if (avalanche)
279
+ break;
280
+ if (signal?.aborted && !succeeded) {
281
+ globalError = 'Execution aborted by caller signal.';
282
+ break;
283
+ }
284
+ if (succeeded) {
285
+ statementsExecuted++;
286
+ onProgress?.({
287
+ kind: 'statement-success',
288
+ statementIndex: i,
289
+ totalStatements: total,
290
+ statementSql: stmtSql,
291
+ attempt: 0,
292
+ elapsedMs: Date.now() - stmtStart,
293
+ });
294
+ }
295
+ else {
296
+ globalError = stmtError ?? 'Unknown execution error.';
297
+ onProgress?.({
298
+ kind: 'statement-failed',
299
+ statementIndex: i,
300
+ totalStatements: total,
301
+ statementSql: stmtSql,
302
+ attempt: maxRetries,
303
+ elapsedMs: Date.now() - stmtStart,
304
+ error: globalError,
305
+ });
306
+ break;
307
+ }
308
+ }
309
+ }
310
+ finally {
311
+ await pool.end().catch(() => { });
312
+ }
313
+ const durationMs = Date.now() - migrationStart;
314
+ const success = !globalError;
315
+ onProgress?.({
316
+ kind: success ? 'migration-complete' : 'migration-failed',
317
+ statementIndex: statementsExecuted,
318
+ totalStatements: total,
319
+ statementSql: '',
320
+ attempt: 0,
321
+ elapsedMs: durationMs,
322
+ error: globalError,
323
+ });
324
+ return {
325
+ success,
326
+ statementsExecuted,
327
+ statementsTotal: total,
328
+ durationMs,
329
+ error: globalError,
330
+ avalanche: avalanche || undefined,
331
+ };
332
+ }
333
+ /**
334
+ * Parses a SQL migration string and returns the list of individual statements
335
+ * that would be executed. Useful for tooling and dry-run previews.
336
+ */
337
+ export function parseMigrationStatements(sql) {
338
+ return splitStatements(sql).map(s => statementText(s.raw));
339
+ }
340
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../src/runner/executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAiB,MAAM,mBAAmB,CAAC;AAmFjF;;;GAGG;AACH,KAAK,UAAU,MAAM;IACnB,IAAI,CAAC;QACH,0EAA0E;QAC1E,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAa,CAAC;QAC3C,OAAO,GAAG,CAAC,OAAO,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,GAAG,GAAG;YACV,EAAE;YACF,gEAAgE;YAChE,+DAA+D;YAC/D,+DAA+D;YAC/D,gEAAgE;YAChE,gEAAgE;YAChE,gEAAgE;YAChE,gEAAgE;YAChE,gEAAgE;YAChE,gEAAgE;YAChE,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,CAAC;AACH,CAAC;AAED,wEAAwE;AACxE,0EAA0E;AAC1E,wEAAwE;AAExE;;;;;;;;;GASG;AACH,SAAS,kBAAkB,CAAC,KAAsB;IAChD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACxB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;GAGG;AACH,SAAS,WAAW,CAAC,GAAY;IAC/B,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;QAC7D,OAAO,MAAM,CAAE,GAA+B,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,wEAAwE;AACxE,0EAA0E;AAC1E,wEAAwE;AAExE;;;;GAIG;AACH,SAAS,aAAa,CAAC,GAAW;IAChC,wEAAwE;IACxE,OAAO,GAAG,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC;AAC7B,CAAC;AAED,wEAAwE;AACxE,0EAA0E;AAC1E,wEAAwE;AAExE;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAW,EACX,OAAwB;IAExB,MAAM,EACJ,WAAW,EACX,WAAW,GAAU,QAAQ,EAC7B,gBAAgB,GAAK,SAAS,EAC9B,UAAU,GAAW,CAAC,EACtB,MAAM,GAAe,KAAK,EAC1B,kBAAkB,GAAG,CAAC,EACtB,aAAa,GAAQ,GAAG,EACxB,UAAU,EACV,MAAM,GACP,GAAG,OAAO,CAAC;IAEZ,MAAM,aAAa,GAAM,kBAAkB,CAAC,WAAW,CAAC,CAAC;IACzD,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;IAEhE,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAElC,uEAAuE;IACvE,MAAM,UAAU,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,KAAK,GAAQ,UAAU,CAAC,MAAM,CAAC;IAErC,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;IACrF,CAAC;IAED,wEAAwE;IACxE,IAAI,MAAM,EAAE,CAAC;QACX,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;YAC3B,UAAU,EAAE,CAAC;gBACX,IAAI,EAAa,mBAAmB;gBACpC,cAAc,EAAG,CAAC;gBAClB,eAAe,EAAE,KAAK;gBACtB,YAAY,EAAK,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC;gBACxC,OAAO,EAAU,CAAC;gBAClB,SAAS,EAAQ,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc;aAC7C,CAAC,CAAC;QACL,CAAC;QACD,OAAO;YACL,OAAO,EAAa,IAAI;YACxB,kBAAkB,EAAE,CAAC;YACrB,eAAe,EAAK,KAAK;YACzB,UAAU,EAAU,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc;SAChD,CAAC;IACJ,CAAC;IAED,wEAAwE;IACxE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,EAAE,CAAC;IAExC,kEAAkE;IAClE,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,gBAAgB,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IAEjE,IAAI,kBAAkB,GAAG,CAAC,CAAC;IAC3B,IAAI,WAA+B,CAAC;IACpC,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,IAAI,CAAC;QACH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3C,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,WAAW,GAAG,qCAAqC,CAAC;gBACpD,MAAM;YACR,CAAC;YAED,MAAM,IAAI,GAAU,UAAU,CAAC,CAAC,CAAC,CAAC;YAClC,MAAM,OAAO,GAAO,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5C,MAAM,SAAS,GAAK,IAAI,CAAC,GAAG,EAAE,CAAC;YAC/B,IAAM,SAAS,GAAK,KAAK,CAAC;YAC1B,IAAM,SAA6B,CAAC;YAEpC,UAAU,EAAE,CAAC;gBACX,IAAI,EAAa,iBAAiB;gBAClC,cAAc,EAAG,CAAC;gBAClB,eAAe,EAAE,KAAK;gBACtB,YAAY,EAAK,OAAO;gBACxB,OAAO,EAAU,CAAC;gBAClB,SAAS,EAAQ,SAAS,GAAG,cAAc;gBAC3C,aAAa,EAAI,MAAM,CAAC,aAAa,CAAC;aACvC,CAAC,CAAC;YAEH,qEAAqE;YACrE,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;gBACvD,IAAI,MAAM,EAAE,OAAO;oBAAE,MAAM;gBAE3B,6DAA6D;gBAC7D,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;oBAChB,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,GAAG,CAAC,EAAE;wBAC1C,WAAW,EAAE,GAAG;wBAChB,UAAU,EAAG,MAAM;wBACnB,UAAU;qBACX,CAAC,CAAC;oBAEH,UAAU,EAAE,CAAC;wBACX,IAAI,EAAc,iBAAiB;wBACnC,cAAc,EAAI,CAAC;wBACnB,eAAe,EAAG,KAAK;wBACvB,YAAY,EAAM,OAAO;wBACzB,OAAO;wBACP,SAAS,EAAS,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc;wBAC7C,cAAc,EAAI,OAAO,CAAC,OAAO;qBAClC,CAAC,CAAC;oBAEH,MAAM,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;oBACrC,IAAI,MAAM,EAAE,OAAO;wBAAE,MAAM;gBAC7B,CAAC;gBAED,+CAA+C;gBAC/C,MAAM,MAAM,GAAI,MAAM,IAAI,CAAC,OAAO,EAAyB,CAAC;gBAE5D,gDAAgD;gBAChD,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC;gBACtB,IAAI,CAAC;oBACH,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;oBACvE,YAAY,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAC1D,CAAC;gBAAC,MAAM,CAAC;oBACP,uCAAuC;gBACzC,CAAC;gBAED,2DAA2D;gBAC3D,MAAM,oBAAoB,GAAG,KAAK,IAA4B,EAAE;oBAC9D,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,EAAE,gBAAgB,EAAE,WAAW,EAAE,CAAC,CAAC;oBAChE,MAAO,SAAqD,CAAC,OAAO,EAAE,EAAE,CAAC;oBACzE,OAAO,SAAqC,CAAC;gBAC/C,CAAC,CAAC;gBAEF,MAAM,OAAO,GAAG,YAAY,GAAG,CAAC;oBAC9B,CAAC,CAAC,gBAAgB,CAAC;wBACf,YAAY;wBACZ,aAAa,EAAG,oBAAoB;wBACpC,cAAc,EAAE,aAAa;wBAC7B,cAAc,EAAE,kBAAkB;wBAClC,MAAM;qBACP,CAAC;oBACJ,CAAC,CAAC,IAAI,CAAC;gBAET,IAAI,CAAC;oBACH,mBAAmB;oBACnB,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBAE5B,6DAA6D;oBAC7D,MAAM,MAAM,CAAC,KAAK,CAAC,6BAA6B,aAAa,GAAG,CAAC,CAAC;oBAClE,MAAM,MAAM,CAAC,KAAK,CAAC,kCAAkC,kBAAkB,GAAG,CAAC,CAAC;oBAE5E,4BAA4B;oBAC5B,MAAM,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBAE5B,SAAS;oBACT,MAAM,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;oBAE7B,mDAAmD;oBACnD,OAAO,EAAE,IAAI,EAAE,CAAC;oBAChB,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,MAAM,CAAC;oBACxC,IAAI,SAAS,EAAE,SAAS,EAAE,CAAC;wBACzB,sDAAsD;wBACtD,sDAAsD;wBACtD,+CAA+C;oBACjD,CAAC;oBAED,SAAS,GAAG,IAAI,CAAC;oBACjB,SAAS,GAAG,SAAS,CAAC;oBACtB,MAAM,CAAC,kBAAkB;gBAE3B,CAAC;gBAAC,OAAO,GAAY,EAAE,CAAC;oBACtB,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;oBAE9B,0CAA0C;oBAC1C,OAAO,EAAE,IAAI,EAAE,CAAC;oBAChB,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,MAAM,CAAC;oBAExC,kCAAkC;oBAClC,IAAI,CAAC;wBAAC,MAAM,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;oBAAC,CAAC;oBAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;oBAE9D,IAAI,SAAS,EAAE,SAAS,EAAE,CAAC;wBACzB,mDAAmD;wBACnD,UAAU,EAAE,CAAC;4BACX,IAAI,EAAa,iBAAiB;4BAClC,cAAc,EAAG,CAAC;4BAClB,eAAe,EAAE,KAAK;4BACtB,YAAY,EAAK,OAAO;4BACxB,OAAO;4BACP,SAAS,EAAQ,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc;4BAC5C,KAAK,EAAY,yBAAyB,SAAS,CAAC,YAAY,oCAAoC,SAAS,CAAC,WAAW,GAAG;yBAC7H,CAAC,CAAC;wBACH,WAAW,GAAG,kCAAkC,SAAS,CAAC,YAAY,iDAAiD,YAAY,iDAAiD,CAAC;wBACrL,SAAS,GAAK,IAAI,CAAC;wBACnB,MAAM;oBACR,CAAC;oBAED,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;wBAC9C,6CAA6C;wBAC7C,SAAS,GAAG,IAAI,IAAI,KAAM,GAAa,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;wBACjE,SAAS;oBACX,CAAC;oBAED,+CAA+C;oBAC/C,SAAS,GAAG,IAAI,IAAI,IAAI,KAAK,KAAM,GAAa,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC1E,MAAM;gBAER,CAAC;wBAAS,CAAC;oBACT,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;gBACrB,CAAC;YACH,CAAC,CAAC,iBAAiB;YAEnB,IAAI,SAAS;gBAAE,MAAM;YAErB,IAAI,MAAM,EAAE,OAAO,IAAI,CAAC,SAAS,EAAE,CAAC;gBAClC,WAAW,GAAG,qCAAqC,CAAC;gBACpD,MAAM;YACR,CAAC;YAED,IAAI,SAAS,EAAE,CAAC;gBACd,kBAAkB,EAAE,CAAC;gBACrB,UAAU,EAAE,CAAC;oBACX,IAAI,EAAa,mBAAmB;oBACpC,cAAc,EAAG,CAAC;oBAClB,eAAe,EAAE,KAAK;oBACtB,YAAY,EAAK,OAAO;oBACxB,OAAO,EAAU,CAAC;oBAClB,SAAS,EAAQ,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;iBACxC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,WAAW,GAAG,SAAS,IAAI,0BAA0B,CAAC;gBACtD,UAAU,EAAE,CAAC;oBACX,IAAI,EAAa,kBAAkB;oBACnC,cAAc,EAAG,CAAC;oBAClB,eAAe,EAAE,KAAK;oBACtB,YAAY,EAAK,OAAO;oBACxB,OAAO,EAAU,UAAU;oBAC3B,SAAS,EAAQ,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;oBACvC,KAAK,EAAY,WAAW;iBAC7B,CAAC,CAAC;gBACH,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAA+B,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,UAAU,GAAI,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc,CAAC;IAChD,MAAM,OAAO,GAAO,CAAC,WAAW,CAAC;IAEjC,UAAU,EAAE,CAAC;QACX,IAAI,EAAa,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,kBAAkB;QACpE,cAAc,EAAG,kBAAkB;QACnC,eAAe,EAAE,KAAK;QACtB,YAAY,EAAK,EAAE;QACnB,OAAO,EAAU,CAAC;QAClB,SAAS,EAAQ,UAAU;QAC3B,KAAK,EAAY,WAAW;KAC7B,CAAC,CAAC;IAEH,OAAO;QACL,OAAO;QACP,kBAAkB;QAClB,eAAe,EAAE,KAAK;QACtB,UAAU;QACV,KAAK,EAAM,WAAW;QACtB,SAAS,EAAE,SAAS,IAAI,SAAS;KAClC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAW;IAClD,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * ddlforge - Background lock-queue monitor for PostgreSQL execution supervisor.
3
+ *
4
+ * During DDL execution the monitor periodically queries pg_locks and
5
+ * pg_stat_activity to detect when other backends are queuing behind the
6
+ * migration's exclusive lock. If the queue depth exceeds a configurable
7
+ * threshold the monitor cancels the migration backend via pg_cancel_backend()
8
+ * and sets the `avalanche` flag so the executor can surface a clear error.
9
+ *
10
+ * Design notes
11
+ * ────────────
12
+ * • Uses a separate Client connection so it never shares the migration
13
+ * transaction (avoids deadlock on the monitor itself).
14
+ * • All monitor queries use SHORT local timeouts so a hung Postgres cannot
15
+ * also jam the monitor.
16
+ * • The monitor resolves its returned promise with a MonitorResult when it
17
+ * self-terminates; callers must await that to detect the avalanche flag.
18
+ */
19
+ export interface MonitorOptions {
20
+ /** PID of the migration backend being guarded */
21
+ migrationPid: number;
22
+ /**
23
+ * Factory that returns a fresh client already connected to the database.
24
+ * The monitor will call end() on it when done.
25
+ */
26
+ clientFactory: () => Promise<MonitorClient>;
27
+ /** Milliseconds between each poll (default: 500 ms) */
28
+ pollIntervalMs?: number;
29
+ /**
30
+ * Number of waiting backends that triggers a cancellation
31
+ * (default: 1 – i.e. cancel as soon as even one query is blocked).
32
+ */
33
+ queueThreshold?: number;
34
+ /** AbortSignal: when aborted the monitor stops without cancelling */
35
+ signal?: AbortSignal;
36
+ }
37
+ /**
38
+ * Minimal pg Client interface required by the monitor.
39
+ * Using a structural type allows tests to inject simple mocks.
40
+ */
41
+ export interface MonitorClient {
42
+ query(sql: string, params?: unknown[]): Promise<{
43
+ rows: Record<string, unknown>[];
44
+ }>;
45
+ end(): Promise<void>;
46
+ }
47
+ export interface MonitorResult {
48
+ /** True when the monitor detected a queue avalanche and cancelled the backend */
49
+ avalanche: boolean;
50
+ /** Number of waiting backends detected at cancellation time */
51
+ blockedCount: number;
52
+ /** ISO timestamp at which cancellation was issued (empty if no cancellation) */
53
+ cancelledAt: string;
54
+ /** Reason the monitor stopped */
55
+ stopReason: 'aborted' | 'avalanche' | 'error' | 'external-stop';
56
+ }
57
+ export interface BlockedBackend {
58
+ waiterPid: number;
59
+ waiterQuery: string;
60
+ waiterState: string;
61
+ waitEventType: string;
62
+ waitEvent: string;
63
+ blockerMode: string;
64
+ }
65
+ /**
66
+ * Starts a non-blocking background lock-queue monitor.
67
+ *
68
+ * Returns a Promise<MonitorResult> that settles when the monitor self-stops
69
+ * (either via the AbortSignal, an avalanche cancellation, or an internal error).
70
+ *
71
+ * Also returns a `stop()` function that callers use to gracefully terminate
72
+ * the monitor after successful execution.
73
+ */
74
+ export declare function startLockMonitor(options: MonitorOptions): {
75
+ result: Promise<MonitorResult>;
76
+ stop: () => void;
77
+ };
78
+ /**
79
+ * Determines whether a Postgres error code represents a lock-related failure.
80
+ *
81
+ * 55P03 — lock_not_available (SET LOCAL lock_timeout exceeded)
82
+ * 57014 — query_canceled (SET LOCAL statement_timeout exceeded, or pg_cancel_backend)
83
+ */
84
+ export declare function isLockError(code: string): boolean;
85
+ //# sourceMappingURL=locksMonitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locksMonitor.d.ts","sourceRoot":"","sources":["../../../src/runner/locksMonitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,MAAM,WAAW,cAAc;IAC7B,iDAAiD;IACjD,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,aAAa,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;IAC5C,uDAAuD;IACvD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,qEAAqE;IACrE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAA;KAAE,CAAC,CAAC;IACrF,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,SAAS,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,WAAW,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,UAAU,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,eAAe,CAAC;CACjE;AAqCD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG;IACzD,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/B,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB,CA0HA;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEjD"}