@cnstra/pg-boss 0.3.0 → 0.6.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/README.md +27 -13
- package/dist/chunk-OJUUT4VL.mjs +263 -0
- package/dist/chunk-OJUUT4VL.mjs.map +1 -0
- package/dist/cli.js +256 -24
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +5 -6
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +46 -17
- package/dist/index.d.ts +46 -17
- package/dist/index.js +53 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +52 -12
- package/dist/index.mjs.map +1 -1
- package/dist/postgres-progress.d.mts +25 -28
- package/dist/postgres-progress.d.ts +25 -28
- package/dist/postgres-progress.js +283 -50
- package/dist/postgres-progress.js.map +1 -1
- package/dist/postgres-progress.mjs +28 -25
- package/dist/postgres-progress.mjs.map +1 -1
- package/package.json +17 -10
- package/dist/chunk-XJFC72LU.mjs +0 -35
- package/dist/chunk-XJFC72LU.mjs.map +0 -1
|
@@ -23,49 +23,278 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
23
23
|
var postgres_progress_exports = {};
|
|
24
24
|
__export(postgres_progress_exports, {
|
|
25
25
|
CNSPgBossProgressRepository: () => CNSPgBossProgressRepository,
|
|
26
|
-
|
|
27
|
-
assertSafeTable: () => assertSafeTable,
|
|
28
|
-
ensureSchema: () => ensureSchema,
|
|
29
|
-
pgBossProgressSchemaSql: () => pgBossProgressSchemaSql
|
|
26
|
+
CNSPgBossProgressSchema: () => CNSPgBossProgressSchema
|
|
30
27
|
});
|
|
31
28
|
module.exports = __toCommonJS(postgres_progress_exports);
|
|
32
29
|
|
|
33
30
|
// src/CNSPgBossProgressRepository.ts
|
|
34
31
|
var import_pg = require("pg");
|
|
35
32
|
|
|
36
|
-
//
|
|
37
|
-
var
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
// ../postgres-schema/dist/index.mjs
|
|
34
|
+
var __defProp2 = Object.defineProperty;
|
|
35
|
+
var __defNormalProp2 = /* @__PURE__ */ __name((obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value, "__defNormalProp");
|
|
36
|
+
var __name2 = /* @__PURE__ */ __name((target, value) => __defProp2(target, "name", { value, configurable: true }), "__name");
|
|
37
|
+
var __publicField2 = /* @__PURE__ */ __name((obj, key, value) => __defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value), "__publicField");
|
|
38
|
+
var _a;
|
|
39
|
+
var _CNSPostgresSchemaMigrator = (_a = class {
|
|
40
|
+
constructor(options) {
|
|
41
|
+
__publicField2(this, "options");
|
|
42
|
+
__publicField2(this, "steps");
|
|
43
|
+
var _a4;
|
|
44
|
+
this.options = options;
|
|
45
|
+
this.steps = [
|
|
46
|
+
...options.steps
|
|
47
|
+
].sort((left, right) => left.step - right.step);
|
|
48
|
+
this.steps.forEach((step, index) => {
|
|
49
|
+
if (step.step !== index + 1) throw new Error(`[${options.name}] schema steps must be numbered 1, 2, 3\u2026; found ${step.step} at position ${index + 1}`);
|
|
50
|
+
});
|
|
51
|
+
if (((_a4 = this.steps[0]) == null ? void 0 : _a4.kind) !== "expand") throw new Error(`[${options.name}] schema step 1 must be an expand step`);
|
|
52
|
+
}
|
|
53
|
+
migrationTable(tablePrefix) {
|
|
54
|
+
return `${tablePrefix}${this.options.name}_migration`;
|
|
55
|
+
}
|
|
56
|
+
async migrate(database, tablePrefix, options = {}) {
|
|
57
|
+
await database.transaction(async (connection) => {
|
|
58
|
+
await connection.query("SELECT pg_advisory_xact_lock($1, hashtext($2))", [
|
|
59
|
+
_a.LOCK_NAMESPACE,
|
|
60
|
+
tablePrefix + this.options.name
|
|
61
|
+
]);
|
|
62
|
+
await connection.query(`SET LOCAL lock_timeout = '${_a.LOCK_TIMEOUT}'`);
|
|
63
|
+
const applied = await this.readAppliedSteps(connection, tablePrefix);
|
|
64
|
+
if (applied === void 0) await this.assertNoUntrackedTables(connection, tablePrefix);
|
|
65
|
+
const pending = this.pendingSteps(applied != null ? applied : /* @__PURE__ */ new Map(), applied === void 0 || options.contract === true);
|
|
66
|
+
if (pending.length > 0) await connection.query(this.sqlFor(tablePrefix, pending, applied === void 0));
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async verify(connection, tablePrefix) {
|
|
70
|
+
const applied = await this.readAppliedSteps(connection, tablePrefix);
|
|
71
|
+
if (applied === void 0) {
|
|
72
|
+
throw new Error(`[${this.options.name}] the schema is not installed under prefix "${tablePrefix}": run migrate()`);
|
|
73
|
+
}
|
|
74
|
+
const missing = this.steps.filter((step) => step.kind === "expand" && !applied.has(step.step)).map((step) => step.step);
|
|
75
|
+
if (missing.length > 0) {
|
|
76
|
+
throw new Error(`[${this.options.name}] schema steps ${missing.join(", ")} are not applied under prefix "${tablePrefix}": run migrate()`);
|
|
77
|
+
}
|
|
78
|
+
const unknown = [
|
|
79
|
+
...applied
|
|
80
|
+
].filter(([step, kind]) => kind === "contract" && step > this.steps.length).map(([step]) => step);
|
|
81
|
+
if (unknown.length > 0) {
|
|
82
|
+
throw new Error(`[${this.options.name}] the database has contract steps ${unknown.join(", ")} that this version does not know: upgrade the package`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
migrationSql(tablePrefix, appliedSteps = [], options = {}) {
|
|
86
|
+
const fresh = appliedSteps.length === 0;
|
|
87
|
+
const applied = new Map(appliedSteps.map((step) => [
|
|
88
|
+
step,
|
|
89
|
+
"expand"
|
|
90
|
+
]));
|
|
91
|
+
return this.sqlFor(tablePrefix, this.pendingSteps(applied, fresh || options.contract === true), fresh);
|
|
92
|
+
}
|
|
93
|
+
assertTablePrefix(tablePrefix, longestSuffix) {
|
|
94
|
+
const longest = Math.max(longestSuffix.length, `${this.options.name}_migration`.length);
|
|
95
|
+
const limit = _a.MAX_IDENTIFIER_LENGTH - longest;
|
|
96
|
+
if (!new RegExp(`^[A-Za-z_][A-Za-z0-9_]{0,${limit - 1}}$`).test(tablePrefix)) {
|
|
97
|
+
throw new Error(`[${this.options.name}] unsafe table prefix ${JSON.stringify(tablePrefix)}: letters, digits and _ only, at most ${limit} characters`);
|
|
98
|
+
}
|
|
99
|
+
return tablePrefix;
|
|
100
|
+
}
|
|
101
|
+
pendingSteps(applied, withContract) {
|
|
102
|
+
return this.steps.filter((step) => !applied.has(step.step) && (step.kind === "expand" || withContract));
|
|
103
|
+
}
|
|
104
|
+
sqlFor(tablePrefix, steps, createMigrationTable) {
|
|
105
|
+
const table = this.migrationTable(tablePrefix);
|
|
106
|
+
const parts = createMigrationTable ? [
|
|
107
|
+
`CREATE TABLE ${table} (step integer PRIMARY KEY, kind text NOT NULL CHECK (kind IN ('expand', 'contract')));`
|
|
108
|
+
] : [];
|
|
109
|
+
for (const step of steps) {
|
|
110
|
+
parts.push(step.sql(tablePrefix), `INSERT INTO ${table} (step, kind) VALUES (${step.step}, '${step.kind}');`);
|
|
111
|
+
}
|
|
112
|
+
return parts.join("\n");
|
|
113
|
+
}
|
|
114
|
+
async readAppliedSteps(connection, tablePrefix) {
|
|
115
|
+
var _a4;
|
|
116
|
+
const table = this.migrationTable(tablePrefix);
|
|
117
|
+
const exists = await connection.query("SELECT to_regclass($1)::text AS found", [
|
|
118
|
+
table
|
|
119
|
+
]);
|
|
120
|
+
if (((_a4 = exists.rows[0]) == null ? void 0 : _a4.found) == null) return void 0;
|
|
121
|
+
const rows = await connection.query(`SELECT step, kind FROM ${table}`);
|
|
122
|
+
return new Map(rows.rows.map((row) => [
|
|
123
|
+
Number(row.step),
|
|
124
|
+
row.kind
|
|
125
|
+
]));
|
|
126
|
+
}
|
|
127
|
+
async assertNoUntrackedTables(connection, tablePrefix) {
|
|
128
|
+
const tables = this.options.tables(tablePrefix);
|
|
129
|
+
const found = await connection.query("SELECT name FROM unnest($1::text[]) AS name WHERE to_regclass(name) IS NOT NULL", [
|
|
130
|
+
tables
|
|
131
|
+
]);
|
|
132
|
+
if (found.rows.length > 0) {
|
|
133
|
+
throw new Error(`[${this.options.name}] tables ${found.rows.map((row) => row.name).join(", ")} exist without ${this.migrationTable(tablePrefix)}: they were created by an earlier unversioned release; drop them or move the data before migrate()`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}, __name(_a, "_CNSPostgresSchemaMigrator"), _a);
|
|
137
|
+
__name2(_CNSPostgresSchemaMigrator, "CNSPostgresSchemaMigrator");
|
|
138
|
+
__publicField2(_CNSPostgresSchemaMigrator, "LOCK_NAMESPACE", 6);
|
|
139
|
+
__publicField2(_CNSPostgresSchemaMigrator, "LOCK_TIMEOUT", "5s");
|
|
140
|
+
__publicField2(_CNSPostgresSchemaMigrator, "MAX_IDENTIFIER_LENGTH", 63);
|
|
141
|
+
var CNSPostgresSchemaMigrator = _CNSPostgresSchemaMigrator;
|
|
142
|
+
var _a2;
|
|
143
|
+
var _CNSPostgresSchemaPgDatabase = (_a2 = class {
|
|
144
|
+
constructor(open) {
|
|
145
|
+
__publicField2(this, "open");
|
|
146
|
+
this.open = open;
|
|
147
|
+
}
|
|
148
|
+
static fromPool(pool) {
|
|
149
|
+
return new _a2(async () => {
|
|
150
|
+
const client = await pool.connect();
|
|
151
|
+
return {
|
|
152
|
+
connection: client,
|
|
153
|
+
release: /* @__PURE__ */ __name2((error) => client.release(error), "release")
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
async transaction(work) {
|
|
158
|
+
const { connection, release } = await this.open();
|
|
159
|
+
try {
|
|
160
|
+
await connection.query("BEGIN");
|
|
161
|
+
const result = await work(connection);
|
|
162
|
+
await connection.query("COMMIT");
|
|
163
|
+
release();
|
|
164
|
+
return result;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
const rollback = await connection.query("ROLLBACK").then(() => void 0, (rollbackError) => rollbackError);
|
|
167
|
+
release(rollback instanceof Error ? rollback : void 0);
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}, __name(_a2, "_CNSPostgresSchemaPgDatabase"), _a2);
|
|
172
|
+
__name2(_CNSPostgresSchemaPgDatabase, "CNSPostgresSchemaPgDatabase");
|
|
173
|
+
var CNSPostgresSchemaPgDatabase = _CNSPostgresSchemaPgDatabase;
|
|
174
|
+
var _a3;
|
|
175
|
+
var _CNSPostgresSchemaPartitionRotation = (_a3 = class {
|
|
176
|
+
constructor(options) {
|
|
177
|
+
__publicField2(this, "options");
|
|
178
|
+
__publicField2(this, "rotatedFor", /* @__PURE__ */ new Map());
|
|
179
|
+
this.options = options;
|
|
180
|
+
if (!(options.premakeDays >= 1) || !Number.isInteger(options.premakeDays)) {
|
|
181
|
+
throw new Error(`[partition rotation] premakeDays must be a positive integer, got ${options.premakeDays}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async rotate(database, table, now, dropBefore, beforeDrop) {
|
|
185
|
+
const today = Math.floor(now / _a3.DAY_MS);
|
|
186
|
+
const days = `${today}:${Math.floor(dropBefore / _a3.DAY_MS)}`;
|
|
187
|
+
if (this.rotatedFor.get(table) === days) return [];
|
|
188
|
+
const dropped = await this.underLock(database, table, async (connection) => {
|
|
189
|
+
const expired = (await this.readRangePartitions(connection, table)).filter((partition) => partition.to <= dropBefore);
|
|
190
|
+
if (beforeDrop) for (const partition of expired) await beforeDrop(connection, partition.name);
|
|
191
|
+
for (const partition of expired) await connection.query(`DROP TABLE ${partition.name}`);
|
|
192
|
+
return expired.map((partition) => partition.name);
|
|
193
|
+
});
|
|
194
|
+
await this.underLock(database, table, async (connection) => {
|
|
195
|
+
const partitions = await this.readRangePartitions(connection, table);
|
|
196
|
+
for (let ahead = 1; ahead <= this.options.premakeDays; ahead++) {
|
|
197
|
+
const from = (today + ahead) * _a3.DAY_MS;
|
|
198
|
+
const to = from + _a3.DAY_MS;
|
|
199
|
+
if (partitions.some((partition) => partition.from < to && from < partition.to)) continue;
|
|
200
|
+
const name = `${table}_p${new Date(from).toISOString().slice(0, 10).replace(/-/g, "")}`;
|
|
201
|
+
await connection.query(`CREATE TABLE ${name} (LIKE ${table} INCLUDING DEFAULTS INCLUDING CONSTRAINTS)`);
|
|
202
|
+
await connection.query(`ALTER TABLE ${table} ATTACH PARTITION ${name} FOR VALUES FROM (${from}) TO (${to})`);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
this.rotatedFor.set(table, days);
|
|
206
|
+
return dropped;
|
|
207
|
+
}
|
|
208
|
+
underLock(database, table, work) {
|
|
209
|
+
return database.transaction(async (connection) => {
|
|
210
|
+
await connection.query("SELECT pg_advisory_xact_lock($1, hashtext($2))", [
|
|
211
|
+
_a3.LOCK_NAMESPACE,
|
|
212
|
+
table
|
|
213
|
+
]);
|
|
214
|
+
await connection.query(`SET LOCAL lock_timeout = '${_a3.LOCK_TIMEOUT}'`);
|
|
215
|
+
return work(connection);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
async readRangePartitions(connection, table) {
|
|
219
|
+
const rows = await connection.query(`SELECT child.oid::regclass::text AS name, pg_get_expr(child.relpartbound, child.oid) AS bound
|
|
220
|
+
FROM pg_inherits
|
|
221
|
+
JOIN pg_class child ON child.oid = pg_inherits.inhrelid
|
|
222
|
+
WHERE pg_inherits.inhparent = $1::regclass`, [
|
|
223
|
+
table
|
|
224
|
+
]);
|
|
225
|
+
const partitions = [];
|
|
226
|
+
for (const row of rows.rows) {
|
|
227
|
+
const range = /^FOR VALUES FROM \('?(-?[\d.e+]+)'?\) TO \('?(-?[\d.e+]+)'?\)$/.exec(row.bound);
|
|
228
|
+
if (range) partitions.push({
|
|
229
|
+
name: row.name,
|
|
230
|
+
from: Number(range[1]),
|
|
231
|
+
to: Number(range[2])
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return partitions;
|
|
235
|
+
}
|
|
236
|
+
}, __name(_a3, "_CNSPostgresSchemaPartitionRotation"), _a3);
|
|
237
|
+
__name2(_CNSPostgresSchemaPartitionRotation, "CNSPostgresSchemaPartitionRotation");
|
|
238
|
+
__publicField2(_CNSPostgresSchemaPartitionRotation, "LOCK_NAMESPACE", 8);
|
|
239
|
+
__publicField2(_CNSPostgresSchemaPartitionRotation, "LOCK_TIMEOUT", "5s");
|
|
240
|
+
__publicField2(_CNSPostgresSchemaPartitionRotation, "DAY_MS", 24 * 60 * 60 * 1e3);
|
|
241
|
+
|
|
242
|
+
// src/schema/CNSPgBossProgressSchema.ts
|
|
243
|
+
var _CNSPgBossProgressSchema = class _CNSPgBossProgressSchema {
|
|
244
|
+
static assertTablePrefix(tablePrefix) {
|
|
245
|
+
return _CNSPgBossProgressSchema.migrator.assertTablePrefix(tablePrefix, _CNSPgBossProgressSchema.LONGEST_SUFFIX);
|
|
246
|
+
}
|
|
247
|
+
static table(tablePrefix = _CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX) {
|
|
248
|
+
return `${_CNSPgBossProgressSchema.assertTablePrefix(tablePrefix)}pgboss_progress`;
|
|
249
|
+
}
|
|
250
|
+
static migrationTable(tablePrefix = _CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX) {
|
|
251
|
+
return _CNSPgBossProgressSchema.migrator.migrationTable(_CNSPgBossProgressSchema.assertTablePrefix(tablePrefix));
|
|
252
|
+
}
|
|
253
|
+
static migrationSql(tablePrefix = _CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX, appliedSteps = [], options) {
|
|
254
|
+
return _CNSPgBossProgressSchema.migrator.migrationSql(_CNSPgBossProgressSchema.assertTablePrefix(tablePrefix), appliedSteps, options);
|
|
255
|
+
}
|
|
256
|
+
static async migrate(pool, tablePrefix = _CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX, options) {
|
|
257
|
+
await _CNSPgBossProgressSchema.migrator.migrate(CNSPostgresSchemaPgDatabase.fromPool(pool), _CNSPgBossProgressSchema.assertTablePrefix(tablePrefix), options);
|
|
258
|
+
}
|
|
259
|
+
static async verify(pool, tablePrefix = _CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX) {
|
|
260
|
+
await _CNSPgBossProgressSchema.migrator.verify(pool, _CNSPgBossProgressSchema.assertTablePrefix(tablePrefix));
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
__name(_CNSPgBossProgressSchema, "CNSPgBossProgressSchema");
|
|
264
|
+
__publicField(_CNSPgBossProgressSchema, "DEFAULT_TABLE_PREFIX", "cns_");
|
|
265
|
+
__publicField(_CNSPgBossProgressSchema, "LONGEST_SUFFIX", "pgboss_progress_pkey");
|
|
266
|
+
__publicField(_CNSPgBossProgressSchema, "migrator", new CNSPostgresSchemaMigrator({
|
|
267
|
+
name: "pgboss_progress",
|
|
268
|
+
tables: /* @__PURE__ */ __name((tablePrefix) => [
|
|
269
|
+
_CNSPgBossProgressSchema.table(tablePrefix)
|
|
270
|
+
], "tables"),
|
|
271
|
+
steps: [
|
|
272
|
+
{
|
|
273
|
+
step: 1,
|
|
274
|
+
kind: "expand",
|
|
275
|
+
sql: /* @__PURE__ */ __name((p) => `
|
|
276
|
+
CREATE TABLE ${p}pgboss_progress (
|
|
277
|
+
job_id text PRIMARY KEY,
|
|
49
278
|
progress jsonb NOT NULL,
|
|
50
|
-
updated_at
|
|
279
|
+
updated_at bigint NOT NULL
|
|
51
280
|
);
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
__name(ensureSchema, "ensureSchema");
|
|
281
|
+
`, "sql")
|
|
282
|
+
}
|
|
283
|
+
]
|
|
284
|
+
}));
|
|
285
|
+
var CNSPgBossProgressSchema = _CNSPgBossProgressSchema;
|
|
59
286
|
|
|
60
287
|
// src/CNSPgBossProgressRepository.ts
|
|
61
288
|
var _CNSPgBossProgressRepository = class _CNSPgBossProgressRepository {
|
|
62
289
|
constructor(opts) {
|
|
63
290
|
__publicField(this, "pool");
|
|
64
291
|
__publicField(this, "ownsPool");
|
|
292
|
+
__publicField(this, "tablePrefix");
|
|
65
293
|
__publicField(this, "table");
|
|
66
294
|
__publicField(this, "autoMigrate");
|
|
295
|
+
__publicField(this, "now");
|
|
67
296
|
__publicField(this, "ready");
|
|
68
|
-
var
|
|
297
|
+
var _a4, _b, _c;
|
|
69
298
|
if (opts.pool) {
|
|
70
299
|
this.pool = opts.pool;
|
|
71
300
|
this.ownsPool = false;
|
|
@@ -77,34 +306,41 @@ var _CNSPgBossProgressRepository = class _CNSPgBossProgressRepository {
|
|
|
77
306
|
} else {
|
|
78
307
|
throw new Error("[@cnstra/pg-boss] CNSPgBossProgressRepository needs `connectionString` or `pool`");
|
|
79
308
|
}
|
|
80
|
-
this.
|
|
309
|
+
this.tablePrefix = CNSPgBossProgressSchema.assertTablePrefix((_a4 = opts.tablePrefix) != null ? _a4 : CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX);
|
|
310
|
+
this.table = CNSPgBossProgressSchema.table(this.tablePrefix);
|
|
81
311
|
this.autoMigrate = (_b = opts.autoMigrate) != null ? _b : true;
|
|
312
|
+
this.now = (_c = opts.now) != null ? _c : Date.now;
|
|
82
313
|
}
|
|
83
|
-
|
|
84
|
-
var
|
|
314
|
+
schemaReady() {
|
|
315
|
+
var _a4;
|
|
85
316
|
if (!this.autoMigrate) return Promise.resolve();
|
|
86
|
-
|
|
317
|
+
(_a4 = this.ready) != null ? _a4 : this.ready = CNSPgBossProgressSchema.migrate(this.pool, this.tablePrefix).catch((error) => {
|
|
318
|
+
this.ready = void 0;
|
|
319
|
+
throw error;
|
|
320
|
+
});
|
|
321
|
+
return this.ready;
|
|
87
322
|
}
|
|
88
323
|
async save(key, progress) {
|
|
89
|
-
await this.
|
|
90
|
-
await this.pool.query(`INSERT INTO ${this.table} (
|
|
91
|
-
VALUES ($1, $2::jsonb,
|
|
92
|
-
ON CONFLICT (
|
|
324
|
+
await this.schemaReady();
|
|
325
|
+
await this.pool.query(`INSERT INTO ${this.table} (job_id, progress, updated_at)
|
|
326
|
+
VALUES ($1, $2::jsonb, $3)
|
|
327
|
+
ON CONFLICT (job_id) DO UPDATE SET progress = EXCLUDED.progress, updated_at = EXCLUDED.updated_at`, [
|
|
93
328
|
key,
|
|
94
|
-
JSON.stringify(progress)
|
|
329
|
+
JSON.stringify(progress),
|
|
330
|
+
this.now()
|
|
95
331
|
]);
|
|
96
332
|
}
|
|
97
333
|
async load(key) {
|
|
98
|
-
var
|
|
99
|
-
await this.
|
|
100
|
-
const res = await this.pool.query(`SELECT progress FROM ${this.table} WHERE
|
|
334
|
+
var _a4;
|
|
335
|
+
await this.schemaReady();
|
|
336
|
+
const res = await this.pool.query(`SELECT progress FROM ${this.table} WHERE job_id = $1`, [
|
|
101
337
|
key
|
|
102
338
|
]);
|
|
103
|
-
return (
|
|
339
|
+
return (_a4 = res.rows[0]) == null ? void 0 : _a4.progress;
|
|
104
340
|
}
|
|
105
341
|
async delete(key) {
|
|
106
|
-
await this.
|
|
107
|
-
await this.pool.query(`DELETE FROM ${this.table} WHERE
|
|
342
|
+
await this.schemaReady();
|
|
343
|
+
await this.pool.query(`DELETE FROM ${this.table} WHERE job_id = $1`, [
|
|
108
344
|
key
|
|
109
345
|
]);
|
|
110
346
|
}
|
|
@@ -116,13 +352,13 @@ var _CNSPgBossProgressRepository = class _CNSPgBossProgressRepository {
|
|
|
116
352
|
* retry backoff window and run this on a schedule. Returns the number of rows removed.
|
|
117
353
|
*/
|
|
118
354
|
async deleteStale(olderThanMs) {
|
|
119
|
-
var
|
|
120
|
-
await this.
|
|
355
|
+
var _a4;
|
|
356
|
+
await this.schemaReady();
|
|
121
357
|
const res = await this.pool.query(`DELETE FROM ${this.table}
|
|
122
|
-
WHERE updated_at <
|
|
123
|
-
Math.max(0, Math.floor(olderThanMs))
|
|
358
|
+
WHERE updated_at < $1`, [
|
|
359
|
+
this.now() - Math.max(0, Math.floor(olderThanMs))
|
|
124
360
|
]);
|
|
125
|
-
return (
|
|
361
|
+
return (_a4 = res.rowCount) != null ? _a4 : 0;
|
|
126
362
|
}
|
|
127
363
|
/** Close the owned pool (no-op when a pool was injected). */
|
|
128
364
|
async close() {
|
|
@@ -134,9 +370,6 @@ var CNSPgBossProgressRepository = _CNSPgBossProgressRepository;
|
|
|
134
370
|
// Annotate the CommonJS export names for ESM import in node:
|
|
135
371
|
0 && (module.exports = {
|
|
136
372
|
CNSPgBossProgressRepository,
|
|
137
|
-
|
|
138
|
-
assertSafeTable,
|
|
139
|
-
ensureSchema,
|
|
140
|
-
pgBossProgressSchemaSql
|
|
373
|
+
CNSPgBossProgressSchema
|
|
141
374
|
});
|
|
142
375
|
//# sourceMappingURL=postgres-progress.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/postgres-progress.ts","../src/CNSPgBossProgressRepository.ts","../src/migrate.ts"],"sourcesContent":["/**\n * Opt-in Postgres progress store for pg-boss — import from\n * `@cnstra/pg-boss/postgres-progress` (this subpath pulls in `pg`; the main\n * `@cnstra/pg-boss` entry does not).\n */\nexport {\n CNSPgBossProgressRepository,\n type TCNSPgBossProgressRepositoryOptions,\n} from './CNSPgBossProgressRepository';\nexport {\n ensureSchema,\n pgBossProgressSchemaSql,\n assertSafeTable,\n DEFAULT_TABLE,\n type IPgQueryable,\n} from './migrate';\n","/**\n * Postgres-backed resume-progress store for pg-boss, keyed by the pg-boss **job id**.\n *\n * In pg-boss a run = stimulation = job (one entity), so the checkpoint is keyed by\n * `job.id`; there is no separate run store. pg-boss can't hold arbitrary per-hop\n * progress on its job row (its `Job` has data/output/signal but no mutable progress\n * field), so the frontier lives in its own table (`cns_pgboss_progress`, default) on\n * the same Postgres. One upserted row per key; dropped when the job completes.\n *\n * OPTIONAL: this is an opt-in export (`@cnstra/pg-boss/postgres-progress`) — importing\n * `@cnstra/pg-boss` (worker/enqueue) does NOT pull in `pg`. It's just one\n * `ICNSProgressRepository`; the progress store is independent of the broker, so you can\n * run pg-boss with `@cnstra/progress-redis` (or any impl) instead.\n */\nimport { Pool } from 'pg';\nimport type { ICNSProgressRepository } from '@cnstra/persist';\nimport type { TCNSProgress } from '@cnstra/persist-dto';\nimport { DEFAULT_TABLE, assertSafeTable, ensureSchema } from './migrate';\n\nexport interface TCNSPgBossProgressRepositoryOptions {\n /** Connection string (a Pool is created and owned by the repo). */\n connectionString?: string;\n /** Or inject an existing pg Pool (not closed by `close()`). */\n pool?: Pool;\n /** Table name (default `cns_pgboss_progress`). */\n table?: string;\n /** Lazily `ensureSchema` on first use (default true). Set false to manage migrations yourself. */\n autoMigrate?: boolean;\n}\n\nexport class CNSPgBossProgressRepository implements ICNSProgressRepository {\n private readonly pool: Pool;\n private readonly ownsPool: boolean;\n private readonly table: string;\n private readonly autoMigrate: boolean;\n private ready: Promise<void> | undefined;\n\n constructor(opts: TCNSPgBossProgressRepositoryOptions) {\n if (opts.pool) {\n this.pool = opts.pool;\n this.ownsPool = false;\n } else if (opts.connectionString) {\n this.pool = new Pool({ connectionString: opts.connectionString });\n this.ownsPool = true;\n } else {\n throw new Error(\n '[@cnstra/pg-boss] CNSPgBossProgressRepository needs `connectionString` or `pool`'\n );\n }\n this.table = assertSafeTable(opts.table ?? DEFAULT_TABLE);\n this.autoMigrate = opts.autoMigrate ?? true;\n }\n\n private ensure(): Promise<void> {\n if (!this.autoMigrate) return Promise.resolve();\n return (this.ready ??= ensureSchema(this.pool, this.table));\n }\n\n async save(key: string, progress: TCNSProgress): Promise<void> {\n await this.ensure();\n await this.pool.query(\n `INSERT INTO ${this.table} (key, progress, updated_at)\n VALUES ($1, $2::jsonb, now())\n ON CONFLICT (key) DO UPDATE SET progress = EXCLUDED.progress, updated_at = now()`,\n [key, JSON.stringify(progress)]\n );\n }\n\n async load(key: string): Promise<TCNSProgress | undefined> {\n await this.ensure();\n const res = await this.pool.query<{ progress: TCNSProgress }>(\n `SELECT progress FROM ${this.table} WHERE key = $1`,\n [key]\n );\n return res.rows[0]?.progress;\n }\n\n async delete(key: string): Promise<void> {\n await this.ensure();\n await this.pool.query(`DELETE FROM ${this.table} WHERE key = $1`, [key]);\n }\n\n /**\n * Prune rows not touched for `olderThanMs` — the safety net for jobs that failed\n * PERMANENTLY (retries exhausted): their frontier row is never `delete`d (pg-boss\n * has no terminal-failure hook), so it would leak. A still-retrying job keeps its\n * `updated_at` fresh, so choose a threshold comfortably larger than your longest\n * retry backoff window and run this on a schedule. Returns the number of rows removed.\n */\n async deleteStale(olderThanMs: number): Promise<number> {\n await this.ensure();\n const res = await this.pool.query(\n `DELETE FROM ${this.table}\n WHERE updated_at < now() - ($1::bigint * interval '1 millisecond')`,\n [Math.max(0, Math.floor(olderThanMs))]\n );\n return res.rowCount ?? 0;\n }\n\n /** Close the owned pool (no-op when a pool was injected). */\n async close(): Promise<void> {\n if (this.ownsPool) await this.pool.end();\n }\n}\n","/**\n * Schema / migrations for the pg-boss progress table (`cns_pgboss_progress`).\n *\n * The package OWNS this schema so you can manage it explicitly (a migration step in\n * your own tooling) instead of relying on the repository's lazy auto-create. Run it\n * via the `cnstra-pgboss-progress migrate` CLI, or call `ensureSchema(db)` yourself\n * with any pg client/pool.\n */\n\n/** Minimal pg client surface (a `pg` Pool or Client satisfies it). */\nexport interface IPgQueryable {\n query(text: string, params?: unknown[]): Promise<{ rows: unknown[] }>;\n}\n\nexport const DEFAULT_TABLE = 'cns_pgboss_progress';\n\n/** Guard the table identifier (it is interpolated into DDL/DML, not parameterised). */\nexport function assertSafeTable(table: string): string {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(table)) {\n throw new Error(\n `[@cnstra/pg-boss] unsafe progress table name: ${JSON.stringify(table)}`\n );\n }\n return table;\n}\n\n/** The CREATE TABLE for the progress store. One upserted row per key (job id). */\nexport function pgBossProgressSchemaSql(table: string = DEFAULT_TABLE): string {\n assertSafeTable(table);\n // `updated_at` + its index back `deleteStale()`: a row is deleted when its job\n // succeeds, but a job that fails PERMANENTLY (retries exhausted) leaves its row —\n // pg-boss has no terminal-failure hook to key off. Pruning by staleness collects\n // those, and the index keeps the sweep cheap.\n return `CREATE TABLE IF NOT EXISTS ${table} (\n key text PRIMARY KEY,\n progress jsonb NOT NULL,\n updated_at timestamptz NOT NULL DEFAULT now()\n);\nCREATE INDEX IF NOT EXISTS ${table}_updated_idx ON ${table} (updated_at)`;\n}\n\n/** Idempotently create the progress table. Safe to run repeatedly. */\nexport async function ensureSchema(\n db: IPgQueryable,\n table: string = DEFAULT_TABLE\n): Promise<void> {\n await db.query(pgBossProgressSchemaSql(table));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;ACcA,gBAAqB;;;ACAd,IAAMA,gBAAgB;AAGtB,SAASC,gBAAgBC,OAAa;AACzC,MAAI,CAAC,2BAA2BC,KAAKD,KAAAA,GAAQ;AACzC,UAAM,IAAIE,MACN,iDAAiDC,KAAKC,UAAUJ,KAAAA,CAAAA,EAAQ;EAEhF;AACA,SAAOA;AACX;AAPgBD;AAUT,SAASM,wBAAwBL,QAAgBF,eAAa;AACjEC,kBAAgBC,KAAAA;AAKhB,SAAO,8BAA8BA,KAAAA;;;;;6BAKZA,KAAAA,mBAAwBA,KAAAA;AACrD;AAZgBK;AAehB,eAAsBC,aAClBC,IACAP,QAAgBF,eAAa;AAE7B,QAAMS,GAAGC,MAAMH,wBAAwBL,KAAAA,CAAAA;AAC3C;AALsBM;;;ADZf,IAAMG,+BAAN,MAAMA,6BAAAA;EAOT,YAAYC,MAA2C;AANtCC;AACAC;AACAC;AACAC;AACTC;AAnCZ;AAsCQ,QAAIL,KAAKC,MAAM;AACX,WAAKA,OAAOD,KAAKC;AACjB,WAAKC,WAAW;IACpB,WAAWF,KAAKM,kBAAkB;AAC9B,WAAKL,OAAO,IAAIM,eAAK;QAAED,kBAAkBN,KAAKM;MAAiB,CAAA;AAC/D,WAAKJ,WAAW;IACpB,OAAO;AACH,YAAM,IAAIM,MACN,kFAAA;IAER;AACA,SAAKL,QAAQM,iBAAgBT,UAAKG,UAALH,YAAcU,aAAAA;AAC3C,SAAKN,eAAcJ,UAAKI,gBAALJ,YAAoB;EAC3C;EAEQW,SAAwB;AArDpC;AAsDQ,QAAI,CAAC,KAAKP,YAAa,QAAOQ,QAAQC,QAAO;AAC7C,YAAQ,UAAKR,UAAL,iBAAKA,QAAUS,aAAa,KAAKb,MAAM,KAAKE,KAAK;EAC7D;EAEA,MAAMY,KAAKC,KAAaC,UAAuC;AAC3D,UAAM,KAAKN,OAAM;AACjB,UAAM,KAAKV,KAAKiB,MACZ,eAAe,KAAKf,KAAK;;gGAGzB;MAACa;MAAKG,KAAKC,UAAUH,QAAAA;KAAU;EAEvC;EAEA,MAAMI,KAAKL,KAAgD;AApE/D;AAqEQ,UAAM,KAAKL,OAAM;AACjB,UAAMW,MAAM,MAAM,KAAKrB,KAAKiB,MACxB,wBAAwB,KAAKf,KAAK,mBAClC;MAACa;KAAI;AAET,YAAOM,SAAIC,KAAK,CAAA,MAATD,mBAAaL;EACxB;EAEA,MAAMO,OAAOR,KAA4B;AACrC,UAAM,KAAKL,OAAM;AACjB,UAAM,KAAKV,KAAKiB,MAAM,eAAe,KAAKf,KAAK,mBAAmB;MAACa;KAAI;EAC3E;;;;;;;;EASA,MAAMS,YAAYC,aAAsC;AAzF5D;AA0FQ,UAAM,KAAKf,OAAM;AACjB,UAAMW,MAAM,MAAM,KAAKrB,KAAKiB,MACxB,eAAe,KAAKf,KAAK;kFAEzB;MAACwB,KAAKC,IAAI,GAAGD,KAAKE,MAAMH,WAAAA,CAAAA;KAAc;AAE1C,YAAOJ,SAAIQ,aAAJR,YAAgB;EAC3B;;EAGA,MAAMS,QAAuB;AACzB,QAAI,KAAK7B,SAAU,OAAM,KAAKD,KAAK+B,IAAG;EAC1C;AACJ;AAzEajC;AAAN,IAAMA,8BAAN;","names":["DEFAULT_TABLE","assertSafeTable","table","test","Error","JSON","stringify","pgBossProgressSchemaSql","ensureSchema","db","query","CNSPgBossProgressRepository","opts","pool","ownsPool","table","autoMigrate","ready","connectionString","Pool","Error","assertSafeTable","DEFAULT_TABLE","ensure","Promise","resolve","ensureSchema","save","key","progress","query","JSON","stringify","load","res","rows","delete","deleteStale","olderThanMs","Math","max","floor","rowCount","close","end"]}
|
|
1
|
+
{"version":3,"sources":["../src/postgres-progress.ts","../src/CNSPgBossProgressRepository.ts","../../postgres-schema/src/CNSPostgresSchemaMigrator.ts","../../postgres-schema/src/CNSPostgresSchemaPgDatabase.ts","../../postgres-schema/src/CNSPostgresSchemaPartitionRotation.ts","../src/schema/CNSPgBossProgressSchema.ts"],"sourcesContent":["/**\n * Opt-in Postgres progress store for pg-boss — import from\n * `@cnstra/pg-boss/postgres-progress` (this subpath pulls in `pg`; the main\n * `@cnstra/pg-boss` entry does not).\n */\nexport {\n CNSPgBossProgressRepository,\n type TCNSPgBossProgressRepositoryOptions,\n} from './CNSPgBossProgressRepository';\nexport { CNSPgBossProgressSchema } from './schema/CNSPgBossProgressSchema';\nexport type { TCNSPgBossProgressMigrateOptions } from './types/TCNSPgBossProgressMigrateOptions';\n","/**\n * Postgres-backed resume-progress store for pg-boss, keyed by the pg-boss **job id**.\n *\n * In pg-boss a run = stimulation = job (one entity), so the checkpoint is keyed by\n * `job.id`; there is no separate run store. pg-boss can't hold arbitrary per-hop\n * progress on its job row (its `Job` has data/output/signal but no mutable progress\n * field), so the frontier lives in its own table (`<tablePrefix>pgboss_progress`) on\n * the same Postgres. One upserted row per key; dropped when the job completes.\n *\n * OPTIONAL: this is an opt-in export (`@cnstra/pg-boss/postgres-progress`) — importing\n * `@cnstra/pg-boss` (worker/enqueue) does NOT pull in `pg`. It's just one\n * `ICNSProgressRepository`; the progress store is independent of the broker, so you can\n * run pg-boss with `@cnstra/progress-redis` (or any impl) instead.\n */\nimport { Pool } from 'pg';\nimport type { ICNSProgressRepository } from '@cnstra/persist';\nimport type { TCNSProgress } from '@cnstra/persist-dto';\nimport { CNSPgBossProgressSchema } from './schema/CNSPgBossProgressSchema';\n\nexport type TCNSPgBossProgressRepositoryOptions = {\n /** Connection string (a Pool is created and owned by the repo). */\n connectionString?: string;\n /** Or inject an existing pg Pool (not closed by `close()`). */\n pool?: Pool;\n /** Table name prefix (default `cns_`); the table is `<tablePrefix>pgboss_progress`. */\n tablePrefix?: string;\n /** Create or upgrade the table on first use (default true). Set false to manage migrations yourself. */\n autoMigrate?: boolean;\n now?: () => number;\n};\n\nexport class CNSPgBossProgressRepository implements ICNSProgressRepository {\n private readonly pool: Pool;\n private readonly ownsPool: boolean;\n private readonly tablePrefix: string;\n private readonly table: string;\n private readonly autoMigrate: boolean;\n private readonly now: () => number;\n private ready: Promise<void> | undefined;\n\n constructor(opts: TCNSPgBossProgressRepositoryOptions) {\n if (opts.pool) {\n this.pool = opts.pool;\n this.ownsPool = false;\n } else if (opts.connectionString) {\n this.pool = new Pool({ connectionString: opts.connectionString });\n this.ownsPool = true;\n } else {\n throw new Error(\n '[@cnstra/pg-boss] CNSPgBossProgressRepository needs `connectionString` or `pool`'\n );\n }\n this.tablePrefix = CNSPgBossProgressSchema.assertTablePrefix(opts.tablePrefix ?? CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX);\n this.table = CNSPgBossProgressSchema.table(this.tablePrefix);\n this.autoMigrate = opts.autoMigrate ?? true;\n this.now = opts.now ?? Date.now;\n }\n\n private schemaReady(): Promise<void> {\n if (!this.autoMigrate) return Promise.resolve();\n this.ready ??= CNSPgBossProgressSchema.migrate(this.pool, this.tablePrefix).catch(error => {\n this.ready = undefined;\n throw error;\n });\n return this.ready;\n }\n\n async save(key: string, progress: TCNSProgress): Promise<void> {\n await this.schemaReady();\n await this.pool.query(\n `INSERT INTO ${this.table} (job_id, progress, updated_at)\n VALUES ($1, $2::jsonb, $3)\n ON CONFLICT (job_id) DO UPDATE SET progress = EXCLUDED.progress, updated_at = EXCLUDED.updated_at`,\n [key, JSON.stringify(progress), this.now()]\n );\n }\n\n async load(key: string): Promise<TCNSProgress | undefined> {\n await this.schemaReady();\n const res = await this.pool.query<{ progress: TCNSProgress }>(\n `SELECT progress FROM ${this.table} WHERE job_id = $1`,\n [key]\n );\n return res.rows[0]?.progress;\n }\n\n async delete(key: string): Promise<void> {\n await this.schemaReady();\n await this.pool.query(`DELETE FROM ${this.table} WHERE job_id = $1`, [key]);\n }\n\n /**\n * Prune rows not touched for `olderThanMs` — the safety net for jobs that failed\n * PERMANENTLY (retries exhausted): their frontier row is never `delete`d (pg-boss\n * has no terminal-failure hook), so it would leak. A still-retrying job keeps its\n * `updated_at` fresh, so choose a threshold comfortably larger than your longest\n * retry backoff window and run this on a schedule. Returns the number of rows removed.\n */\n async deleteStale(olderThanMs: number): Promise<number> {\n await this.schemaReady();\n const res = await this.pool.query(\n `DELETE FROM ${this.table}\n WHERE updated_at < $1`,\n [this.now() - Math.max(0, Math.floor(olderThanMs))]\n );\n return res.rowCount ?? 0;\n }\n\n /** Close the owned pool (no-op when a pool was injected). */\n async close(): Promise<void> {\n if (this.ownsPool) await this.pool.end();\n }\n}\n","import type { ICNSPostgresSchemaConnection } from './interfaces/ICNSPostgresSchemaConnection';\nimport type { ICNSPostgresSchemaDatabase } from './interfaces/ICNSPostgresSchemaDatabase';\nimport type { ECNSPostgresSchemaStepKind } from './types/ECNSPostgresSchemaStepKind';\nimport type { TCNSPostgresSchemaMigrateOptions } from './types/TCNSPostgresSchemaMigrateOptions';\nimport type { TCNSPostgresSchemaMigratorOptions } from './types/TCNSPostgresSchemaMigratorOptions';\nimport type { TCNSPostgresSchemaStep } from './types/TCNSPostgresSchemaStep';\n\nexport class CNSPostgresSchemaMigrator {\n static readonly LOCK_NAMESPACE = 6;\n static readonly LOCK_TIMEOUT = '5s';\n static readonly MAX_IDENTIFIER_LENGTH = 63;\n\n private readonly steps: readonly TCNSPostgresSchemaStep[];\n\n constructor(private readonly options: TCNSPostgresSchemaMigratorOptions) {\n this.steps = [...options.steps].sort((left, right) => left.step - right.step);\n this.steps.forEach((step, index) => {\n if (step.step !== index + 1) throw new Error(`[${options.name}] schema steps must be numbered 1, 2, 3…; found ${step.step} at position ${index + 1}`);\n });\n if (this.steps[0]?.kind !== 'expand') throw new Error(`[${options.name}] schema step 1 must be an expand step`);\n }\n\n migrationTable(tablePrefix: string): string {\n return `${tablePrefix}${this.options.name}_migration`;\n }\n\n async migrate(database: ICNSPostgresSchemaDatabase, tablePrefix: string, options: TCNSPostgresSchemaMigrateOptions = {}): Promise<void> {\n await database.transaction(async connection => {\n await connection.query('SELECT pg_advisory_xact_lock($1, hashtext($2))', [CNSPostgresSchemaMigrator.LOCK_NAMESPACE, tablePrefix + this.options.name]);\n await connection.query(`SET LOCAL lock_timeout = '${CNSPostgresSchemaMigrator.LOCK_TIMEOUT}'`);\n const applied = await this.readAppliedSteps(connection, tablePrefix);\n if (applied === undefined) await this.assertNoUntrackedTables(connection, tablePrefix);\n const pending = this.pendingSteps(applied ?? new Map(), applied === undefined || options.contract === true);\n if (pending.length > 0) await connection.query(this.sqlFor(tablePrefix, pending, applied === undefined));\n });\n }\n\n async verify(connection: ICNSPostgresSchemaConnection, tablePrefix: string): Promise<void> {\n const applied = await this.readAppliedSteps(connection, tablePrefix);\n if (applied === undefined) {\n throw new Error(`[${this.options.name}] the schema is not installed under prefix \"${tablePrefix}\": run migrate()`);\n }\n const missing = this.steps.filter(step => step.kind === 'expand' && !applied.has(step.step)).map(step => step.step);\n if (missing.length > 0) {\n throw new Error(`[${this.options.name}] schema steps ${missing.join(', ')} are not applied under prefix \"${tablePrefix}\": run migrate()`);\n }\n const unknown = [...applied].filter(([step, kind]) => kind === 'contract' && step > this.steps.length).map(([step]) => step);\n if (unknown.length > 0) {\n throw new Error(`[${this.options.name}] the database has contract steps ${unknown.join(', ')} that this version does not know: upgrade the package`);\n }\n }\n\n migrationSql(tablePrefix: string, appliedSteps: readonly number[] = [], options: TCNSPostgresSchemaMigrateOptions = {}): string {\n const fresh = appliedSteps.length === 0;\n const applied = new Map<number, ECNSPostgresSchemaStepKind>(appliedSteps.map(step => [step, 'expand']));\n return this.sqlFor(tablePrefix, this.pendingSteps(applied, fresh || options.contract === true), fresh);\n }\n\n assertTablePrefix(tablePrefix: string, longestSuffix: string): string {\n const longest = Math.max(longestSuffix.length, `${this.options.name}_migration`.length);\n const limit = CNSPostgresSchemaMigrator.MAX_IDENTIFIER_LENGTH - longest;\n if (!new RegExp(`^[A-Za-z_][A-Za-z0-9_]{0,${limit - 1}}$`).test(tablePrefix)) {\n throw new Error(`[${this.options.name}] unsafe table prefix ${JSON.stringify(tablePrefix)}: letters, digits and _ only, at most ${limit} characters`);\n }\n return tablePrefix;\n }\n\n private pendingSteps(applied: ReadonlyMap<number, ECNSPostgresSchemaStepKind>, withContract: boolean): TCNSPostgresSchemaStep[] {\n return this.steps.filter(step => !applied.has(step.step) && (step.kind === 'expand' || withContract));\n }\n\n private sqlFor(tablePrefix: string, steps: readonly TCNSPostgresSchemaStep[], createMigrationTable: boolean): string {\n const table = this.migrationTable(tablePrefix);\n const parts = createMigrationTable\n ? [`CREATE TABLE ${table} (step integer PRIMARY KEY, kind text NOT NULL CHECK (kind IN ('expand', 'contract')));`]\n : [];\n for (const step of steps) {\n parts.push(step.sql(tablePrefix), `INSERT INTO ${table} (step, kind) VALUES (${step.step}, '${step.kind}');`);\n }\n return parts.join('\\n');\n }\n\n private async readAppliedSteps(connection: ICNSPostgresSchemaConnection, tablePrefix: string): Promise<Map<number, ECNSPostgresSchemaStepKind> | undefined> {\n const table = this.migrationTable(tablePrefix);\n const exists = await connection.query<{ found: string | null }>('SELECT to_regclass($1)::text AS found', [table]);\n if (exists.rows[0]?.found == null) return undefined;\n const rows = await connection.query<{ step: number; kind: ECNSPostgresSchemaStepKind }>(`SELECT step, kind FROM ${table}`);\n return new Map(rows.rows.map(row => [Number(row.step), row.kind]));\n }\n\n private async assertNoUntrackedTables(connection: ICNSPostgresSchemaConnection, tablePrefix: string): Promise<void> {\n const tables = this.options.tables(tablePrefix);\n const found = await connection.query<{ name: string }>(\n 'SELECT name FROM unnest($1::text[]) AS name WHERE to_regclass(name) IS NOT NULL',\n [tables]\n );\n if (found.rows.length > 0) {\n throw new Error(\n `[${this.options.name}] tables ${found.rows.map(row => row.name).join(', ')} exist without ${this.migrationTable(tablePrefix)}: they were created by an earlier unversioned release; drop them or move the data before migrate()`\n );\n }\n }\n}\n","import type { ICNSPostgresSchemaConnection } from './interfaces/ICNSPostgresSchemaConnection';\nimport type { ICNSPostgresSchemaDatabase } from './interfaces/ICNSPostgresSchemaDatabase';\nimport type { ICNSPostgresSchemaPgPool } from './interfaces/ICNSPostgresSchemaPgPool';\n\nexport class CNSPostgresSchemaPgDatabase implements ICNSPostgresSchemaDatabase {\n private constructor(\n private readonly open: () => Promise<{ connection: ICNSPostgresSchemaConnection; release: (error?: Error) => void }>\n ) {}\n\n static fromPool(pool: ICNSPostgresSchemaPgPool): CNSPostgresSchemaPgDatabase {\n return new CNSPostgresSchemaPgDatabase(async () => {\n const client = await pool.connect();\n return { connection: client, release: error => client.release(error) };\n });\n }\n\n async transaction<TResult>(work: (connection: ICNSPostgresSchemaConnection) => Promise<TResult>): Promise<TResult> {\n const { connection, release } = await this.open();\n try {\n await connection.query('BEGIN');\n const result = await work(connection);\n await connection.query('COMMIT');\n release();\n return result;\n } catch (error) {\n const rollback = await connection.query('ROLLBACK').then(\n () => undefined,\n (rollbackError: unknown) => rollbackError\n );\n release(rollback instanceof Error ? rollback : undefined);\n throw error;\n }\n }\n}\n","import type { ICNSPostgresSchemaConnection } from './interfaces/ICNSPostgresSchemaConnection';\nimport type { ICNSPostgresSchemaDatabase } from './interfaces/ICNSPostgresSchemaDatabase';\nimport type { TCNSPostgresSchemaPartitionRotationOptions } from './types/TCNSPostgresSchemaPartitionRotationOptions';\n\ntype TRangePartition = { readonly name: string; readonly from: number; readonly to: number };\n\nexport class CNSPostgresSchemaPartitionRotation {\n static readonly LOCK_NAMESPACE = 8;\n static readonly LOCK_TIMEOUT = '5s';\n static readonly DAY_MS = 24 * 60 * 60 * 1000;\n\n private readonly rotatedFor = new Map<string, string>();\n\n constructor(private readonly options: TCNSPostgresSchemaPartitionRotationOptions) {\n if (!(options.premakeDays >= 1) || !Number.isInteger(options.premakeDays)) {\n throw new Error(`[partition rotation] premakeDays must be a positive integer, got ${options.premakeDays}`);\n }\n }\n\n async rotate(\n database: ICNSPostgresSchemaDatabase,\n table: string,\n now: number,\n dropBefore: number,\n beforeDrop?: (connection: ICNSPostgresSchemaConnection, partition: string) => Promise<void>\n ): Promise<string[]> {\n const today = Math.floor(now / CNSPostgresSchemaPartitionRotation.DAY_MS);\n const days = `${today}:${Math.floor(dropBefore / CNSPostgresSchemaPartitionRotation.DAY_MS)}`;\n if (this.rotatedFor.get(table) === days) return [];\n const dropped = await this.underLock(database, table, async connection => {\n const expired = (await this.readRangePartitions(connection, table)).filter(partition => partition.to <= dropBefore);\n if (beforeDrop) for (const partition of expired) await beforeDrop(connection, partition.name);\n for (const partition of expired) await connection.query(`DROP TABLE ${partition.name}`);\n return expired.map(partition => partition.name);\n });\n await this.underLock(database, table, async connection => {\n const partitions = await this.readRangePartitions(connection, table);\n for (let ahead = 1; ahead <= this.options.premakeDays; ahead++) {\n const from = (today + ahead) * CNSPostgresSchemaPartitionRotation.DAY_MS;\n const to = from + CNSPostgresSchemaPartitionRotation.DAY_MS;\n if (partitions.some(partition => partition.from < to && from < partition.to)) continue;\n const name = `${table}_p${new Date(from).toISOString().slice(0, 10).replace(/-/g, '')}`;\n await connection.query(`CREATE TABLE ${name} (LIKE ${table} INCLUDING DEFAULTS INCLUDING CONSTRAINTS)`);\n await connection.query(`ALTER TABLE ${table} ATTACH PARTITION ${name} FOR VALUES FROM (${from}) TO (${to})`);\n }\n });\n this.rotatedFor.set(table, days);\n return dropped;\n }\n\n private underLock<TResult>(database: ICNSPostgresSchemaDatabase, table: string, work: (connection: ICNSPostgresSchemaConnection) => Promise<TResult>): Promise<TResult> {\n return database.transaction(async connection => {\n await connection.query('SELECT pg_advisory_xact_lock($1, hashtext($2))', [CNSPostgresSchemaPartitionRotation.LOCK_NAMESPACE, table]);\n await connection.query(`SET LOCAL lock_timeout = '${CNSPostgresSchemaPartitionRotation.LOCK_TIMEOUT}'`);\n return work(connection);\n });\n }\n\n private async readRangePartitions(connection: ICNSPostgresSchemaConnection, table: string): Promise<TRangePartition[]> {\n const rows = await connection.query<{ name: string; bound: string }>(\n `SELECT child.oid::regclass::text AS name, pg_get_expr(child.relpartbound, child.oid) AS bound\n FROM pg_inherits\n JOIN pg_class child ON child.oid = pg_inherits.inhrelid\n WHERE pg_inherits.inhparent = $1::regclass`,\n [table]\n );\n const partitions: TRangePartition[] = [];\n for (const row of rows.rows) {\n const range = /^FOR VALUES FROM \\('?(-?[\\d.e+]+)'?\\) TO \\('?(-?[\\d.e+]+)'?\\)$/.exec(row.bound);\n if (range) partitions.push({ name: row.name, from: Number(range[1]), to: Number(range[2]) });\n }\n return partitions;\n }\n}\n","import { CNSPostgresSchemaMigrator, CNSPostgresSchemaPgDatabase } from '@cnstra/postgres-schema';\nimport type { ICNSPostgresSchemaConnection, ICNSPostgresSchemaPgPool } from '@cnstra/postgres-schema';\nimport type { Pool } from 'pg';\nimport type { TCNSPgBossProgressMigrateOptions } from '../types/TCNSPgBossProgressMigrateOptions';\n\nexport class CNSPgBossProgressSchema {\n static readonly DEFAULT_TABLE_PREFIX = 'cns_';\n private static readonly LONGEST_SUFFIX = 'pgboss_progress_pkey';\n\n private static readonly migrator = new CNSPostgresSchemaMigrator({\n name: 'pgboss_progress',\n tables: tablePrefix => [CNSPgBossProgressSchema.table(tablePrefix)],\n steps: [\n {\n step: 1,\n kind: 'expand',\n sql: p => `\nCREATE TABLE ${p}pgboss_progress (\n job_id text PRIMARY KEY,\n progress jsonb NOT NULL,\n updated_at bigint NOT NULL\n);\n`,\n },\n ],\n });\n\n static assertTablePrefix(tablePrefix: string): string {\n return CNSPgBossProgressSchema.migrator.assertTablePrefix(tablePrefix, CNSPgBossProgressSchema.LONGEST_SUFFIX);\n }\n\n static table(tablePrefix: string = CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX): string {\n return `${CNSPgBossProgressSchema.assertTablePrefix(tablePrefix)}pgboss_progress`;\n }\n\n static migrationTable(tablePrefix: string = CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX): string {\n return CNSPgBossProgressSchema.migrator.migrationTable(CNSPgBossProgressSchema.assertTablePrefix(tablePrefix));\n }\n\n static migrationSql(\n tablePrefix: string = CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX,\n appliedSteps: readonly number[] = [],\n options?: TCNSPgBossProgressMigrateOptions\n ): string {\n return CNSPgBossProgressSchema.migrator.migrationSql(CNSPgBossProgressSchema.assertTablePrefix(tablePrefix), appliedSteps, options);\n }\n\n static async migrate(\n pool: Pool,\n tablePrefix: string = CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX,\n options?: TCNSPgBossProgressMigrateOptions\n ): Promise<void> {\n await CNSPgBossProgressSchema.migrator.migrate(\n CNSPostgresSchemaPgDatabase.fromPool(pool as unknown as ICNSPostgresSchemaPgPool),\n CNSPgBossProgressSchema.assertTablePrefix(tablePrefix),\n options\n );\n }\n\n static async verify(pool: Pool, tablePrefix: string = CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX): Promise<void> {\n await CNSPgBossProgressSchema.migrator.verify(\n pool as unknown as ICNSPostgresSchemaConnection,\n CNSPgBossProgressSchema.assertTablePrefix(tablePrefix)\n );\n }\n}\n"],"mappings":"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","names":["CNSPostgresSchemaMigrator","options","steps","_a","sort","left","right","step","forEach","index","Error","name","kind","migrationTable","tablePrefix","migrate","database","transaction","connection","query","LOCK_NAMESPACE","LOCK_TIMEOUT","applied","readAppliedSteps","undefined","assertNoUntrackedTables","pending","pendingSteps","Map","contract","length","sqlFor","verify","missing","filter","has","map","join","unknown","migrationSql","appliedSteps","fresh","assertTablePrefix","longestSuffix","longest","Math","max","limit","MAX_IDENTIFIER_LENGTH","RegExp","test","JSON","stringify","withContract","createMigrationTable","table","parts","push","sql","exists","rows","found","row","Number","tables","__publicField","CNSPostgresSchemaPgDatabase","open","fromPool","pool","client","connect","release","error","work","result","rollback","then","rollbackError","CNSPostgresSchemaPartitionRotation","rotatedFor","premakeDays","isInteger","rotate","now","dropBefore","beforeDrop","today","floor","DAY_MS","days","get","dropped","underLock","expired","readRangePartitions","partition","to","partitions","ahead","from","some","Date","toISOString","slice","replace","set","range","exec","bound","CNSPgBossProgressSchema","assertTablePrefix","tablePrefix","migrator","LONGEST_SUFFIX","table","DEFAULT_TABLE_PREFIX","migrationTable","migrationSql","appliedSteps","options","migrate","pool","CNSPostgresSchemaPgDatabase","fromPool","verify","CNSPostgresSchemaMigrator","name","tables","steps","step","kind","sql","p","CNSPgBossProgressRepository","opts","pool","ownsPool","tablePrefix","table","autoMigrate","now","ready","_a","connectionString","Pool","Error","CNSPgBossProgressSchema","assertTablePrefix","DEFAULT_TABLE_PREFIX","Date","schemaReady","Promise","resolve","migrate","catch","error","undefined","save","key","progress","query","JSON","stringify","load","res","rows","delete","deleteStale","olderThanMs","Math","max","floor","rowCount","close","end"]}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
ensureSchema,
|
|
5
|
-
pgBossProgressSchemaSql
|
|
6
|
-
} from "./chunk-XJFC72LU.mjs";
|
|
2
|
+
CNSPgBossProgressSchema
|
|
3
|
+
} from "./chunk-OJUUT4VL.mjs";
|
|
7
4
|
import {
|
|
8
5
|
__name,
|
|
9
6
|
__publicField
|
|
@@ -15,10 +12,12 @@ var _CNSPgBossProgressRepository = class _CNSPgBossProgressRepository {
|
|
|
15
12
|
constructor(opts) {
|
|
16
13
|
__publicField(this, "pool");
|
|
17
14
|
__publicField(this, "ownsPool");
|
|
15
|
+
__publicField(this, "tablePrefix");
|
|
18
16
|
__publicField(this, "table");
|
|
19
17
|
__publicField(this, "autoMigrate");
|
|
18
|
+
__publicField(this, "now");
|
|
20
19
|
__publicField(this, "ready");
|
|
21
|
-
var _a, _b;
|
|
20
|
+
var _a, _b, _c;
|
|
22
21
|
if (opts.pool) {
|
|
23
22
|
this.pool = opts.pool;
|
|
24
23
|
this.ownsPool = false;
|
|
@@ -30,34 +29,41 @@ var _CNSPgBossProgressRepository = class _CNSPgBossProgressRepository {
|
|
|
30
29
|
} else {
|
|
31
30
|
throw new Error("[@cnstra/pg-boss] CNSPgBossProgressRepository needs `connectionString` or `pool`");
|
|
32
31
|
}
|
|
33
|
-
this.
|
|
32
|
+
this.tablePrefix = CNSPgBossProgressSchema.assertTablePrefix((_a = opts.tablePrefix) != null ? _a : CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX);
|
|
33
|
+
this.table = CNSPgBossProgressSchema.table(this.tablePrefix);
|
|
34
34
|
this.autoMigrate = (_b = opts.autoMigrate) != null ? _b : true;
|
|
35
|
+
this.now = (_c = opts.now) != null ? _c : Date.now;
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
+
schemaReady() {
|
|
37
38
|
var _a;
|
|
38
39
|
if (!this.autoMigrate) return Promise.resolve();
|
|
39
|
-
|
|
40
|
+
(_a = this.ready) != null ? _a : this.ready = CNSPgBossProgressSchema.migrate(this.pool, this.tablePrefix).catch((error) => {
|
|
41
|
+
this.ready = void 0;
|
|
42
|
+
throw error;
|
|
43
|
+
});
|
|
44
|
+
return this.ready;
|
|
40
45
|
}
|
|
41
46
|
async save(key, progress) {
|
|
42
|
-
await this.
|
|
43
|
-
await this.pool.query(`INSERT INTO ${this.table} (
|
|
44
|
-
VALUES ($1, $2::jsonb,
|
|
45
|
-
ON CONFLICT (
|
|
47
|
+
await this.schemaReady();
|
|
48
|
+
await this.pool.query(`INSERT INTO ${this.table} (job_id, progress, updated_at)
|
|
49
|
+
VALUES ($1, $2::jsonb, $3)
|
|
50
|
+
ON CONFLICT (job_id) DO UPDATE SET progress = EXCLUDED.progress, updated_at = EXCLUDED.updated_at`, [
|
|
46
51
|
key,
|
|
47
|
-
JSON.stringify(progress)
|
|
52
|
+
JSON.stringify(progress),
|
|
53
|
+
this.now()
|
|
48
54
|
]);
|
|
49
55
|
}
|
|
50
56
|
async load(key) {
|
|
51
57
|
var _a;
|
|
52
|
-
await this.
|
|
53
|
-
const res = await this.pool.query(`SELECT progress FROM ${this.table} WHERE
|
|
58
|
+
await this.schemaReady();
|
|
59
|
+
const res = await this.pool.query(`SELECT progress FROM ${this.table} WHERE job_id = $1`, [
|
|
54
60
|
key
|
|
55
61
|
]);
|
|
56
62
|
return (_a = res.rows[0]) == null ? void 0 : _a.progress;
|
|
57
63
|
}
|
|
58
64
|
async delete(key) {
|
|
59
|
-
await this.
|
|
60
|
-
await this.pool.query(`DELETE FROM ${this.table} WHERE
|
|
65
|
+
await this.schemaReady();
|
|
66
|
+
await this.pool.query(`DELETE FROM ${this.table} WHERE job_id = $1`, [
|
|
61
67
|
key
|
|
62
68
|
]);
|
|
63
69
|
}
|
|
@@ -70,10 +76,10 @@ var _CNSPgBossProgressRepository = class _CNSPgBossProgressRepository {
|
|
|
70
76
|
*/
|
|
71
77
|
async deleteStale(olderThanMs) {
|
|
72
78
|
var _a;
|
|
73
|
-
await this.
|
|
79
|
+
await this.schemaReady();
|
|
74
80
|
const res = await this.pool.query(`DELETE FROM ${this.table}
|
|
75
|
-
WHERE updated_at <
|
|
76
|
-
Math.max(0, Math.floor(olderThanMs))
|
|
81
|
+
WHERE updated_at < $1`, [
|
|
82
|
+
this.now() - Math.max(0, Math.floor(olderThanMs))
|
|
77
83
|
]);
|
|
78
84
|
return (_a = res.rowCount) != null ? _a : 0;
|
|
79
85
|
}
|
|
@@ -86,9 +92,6 @@ __name(_CNSPgBossProgressRepository, "CNSPgBossProgressRepository");
|
|
|
86
92
|
var CNSPgBossProgressRepository = _CNSPgBossProgressRepository;
|
|
87
93
|
export {
|
|
88
94
|
CNSPgBossProgressRepository,
|
|
89
|
-
|
|
90
|
-
assertSafeTable,
|
|
91
|
-
ensureSchema,
|
|
92
|
-
pgBossProgressSchemaSql
|
|
95
|
+
CNSPgBossProgressSchema
|
|
93
96
|
};
|
|
94
97
|
//# sourceMappingURL=postgres-progress.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/CNSPgBossProgressRepository.ts"],"sourcesContent":["/**\n * Postgres-backed resume-progress store for pg-boss, keyed by the pg-boss **job id**.\n *\n * In pg-boss a run = stimulation = job (one entity), so the checkpoint is keyed by\n * `job.id`; there is no separate run store. pg-boss can't hold arbitrary per-hop\n * progress on its job row (its `Job` has data/output/signal but no mutable progress\n * field), so the frontier lives in its own table (`
|
|
1
|
+
{"version":3,"sources":["../src/CNSPgBossProgressRepository.ts"],"sourcesContent":["/**\n * Postgres-backed resume-progress store for pg-boss, keyed by the pg-boss **job id**.\n *\n * In pg-boss a run = stimulation = job (one entity), so the checkpoint is keyed by\n * `job.id`; there is no separate run store. pg-boss can't hold arbitrary per-hop\n * progress on its job row (its `Job` has data/output/signal but no mutable progress\n * field), so the frontier lives in its own table (`<tablePrefix>pgboss_progress`) on\n * the same Postgres. One upserted row per key; dropped when the job completes.\n *\n * OPTIONAL: this is an opt-in export (`@cnstra/pg-boss/postgres-progress`) — importing\n * `@cnstra/pg-boss` (worker/enqueue) does NOT pull in `pg`. It's just one\n * `ICNSProgressRepository`; the progress store is independent of the broker, so you can\n * run pg-boss with `@cnstra/progress-redis` (or any impl) instead.\n */\nimport { Pool } from 'pg';\nimport type { ICNSProgressRepository } from '@cnstra/persist';\nimport type { TCNSProgress } from '@cnstra/persist-dto';\nimport { CNSPgBossProgressSchema } from './schema/CNSPgBossProgressSchema';\n\nexport type TCNSPgBossProgressRepositoryOptions = {\n /** Connection string (a Pool is created and owned by the repo). */\n connectionString?: string;\n /** Or inject an existing pg Pool (not closed by `close()`). */\n pool?: Pool;\n /** Table name prefix (default `cns_`); the table is `<tablePrefix>pgboss_progress`. */\n tablePrefix?: string;\n /** Create or upgrade the table on first use (default true). Set false to manage migrations yourself. */\n autoMigrate?: boolean;\n now?: () => number;\n};\n\nexport class CNSPgBossProgressRepository implements ICNSProgressRepository {\n private readonly pool: Pool;\n private readonly ownsPool: boolean;\n private readonly tablePrefix: string;\n private readonly table: string;\n private readonly autoMigrate: boolean;\n private readonly now: () => number;\n private ready: Promise<void> | undefined;\n\n constructor(opts: TCNSPgBossProgressRepositoryOptions) {\n if (opts.pool) {\n this.pool = opts.pool;\n this.ownsPool = false;\n } else if (opts.connectionString) {\n this.pool = new Pool({ connectionString: opts.connectionString });\n this.ownsPool = true;\n } else {\n throw new Error(\n '[@cnstra/pg-boss] CNSPgBossProgressRepository needs `connectionString` or `pool`'\n );\n }\n this.tablePrefix = CNSPgBossProgressSchema.assertTablePrefix(opts.tablePrefix ?? CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX);\n this.table = CNSPgBossProgressSchema.table(this.tablePrefix);\n this.autoMigrate = opts.autoMigrate ?? true;\n this.now = opts.now ?? Date.now;\n }\n\n private schemaReady(): Promise<void> {\n if (!this.autoMigrate) return Promise.resolve();\n this.ready ??= CNSPgBossProgressSchema.migrate(this.pool, this.tablePrefix).catch(error => {\n this.ready = undefined;\n throw error;\n });\n return this.ready;\n }\n\n async save(key: string, progress: TCNSProgress): Promise<void> {\n await this.schemaReady();\n await this.pool.query(\n `INSERT INTO ${this.table} (job_id, progress, updated_at)\n VALUES ($1, $2::jsonb, $3)\n ON CONFLICT (job_id) DO UPDATE SET progress = EXCLUDED.progress, updated_at = EXCLUDED.updated_at`,\n [key, JSON.stringify(progress), this.now()]\n );\n }\n\n async load(key: string): Promise<TCNSProgress | undefined> {\n await this.schemaReady();\n const res = await this.pool.query<{ progress: TCNSProgress }>(\n `SELECT progress FROM ${this.table} WHERE job_id = $1`,\n [key]\n );\n return res.rows[0]?.progress;\n }\n\n async delete(key: string): Promise<void> {\n await this.schemaReady();\n await this.pool.query(`DELETE FROM ${this.table} WHERE job_id = $1`, [key]);\n }\n\n /**\n * Prune rows not touched for `olderThanMs` — the safety net for jobs that failed\n * PERMANENTLY (retries exhausted): their frontier row is never `delete`d (pg-boss\n * has no terminal-failure hook), so it would leak. A still-retrying job keeps its\n * `updated_at` fresh, so choose a threshold comfortably larger than your longest\n * retry backoff window and run this on a schedule. Returns the number of rows removed.\n */\n async deleteStale(olderThanMs: number): Promise<number> {\n await this.schemaReady();\n const res = await this.pool.query(\n `DELETE FROM ${this.table}\n WHERE updated_at < $1`,\n [this.now() - Math.max(0, Math.floor(olderThanMs))]\n );\n return res.rowCount ?? 0;\n }\n\n /** Close the owned pool (no-op when a pool was injected). */\n async close(): Promise<void> {\n if (this.ownsPool) await this.pool.end();\n }\n}\n"],"mappings":";;;;;;;;;AAcA,SAASA,YAAY;AAiBd,IAAMC,+BAAN,MAAMA,6BAAAA;EAST,YAAYC,MAA2C;AARtCC;AACAC;AACAC;AACAC;AACAC;AACAC;AACTC;AAtCZ;AAyCQ,QAAIP,KAAKC,MAAM;AACX,WAAKA,OAAOD,KAAKC;AACjB,WAAKC,WAAW;IACpB,WAAWF,KAAKQ,kBAAkB;AAC9B,WAAKP,OAAO,IAAIQ,KAAK;QAAED,kBAAkBR,KAAKQ;MAAiB,CAAA;AAC/D,WAAKN,WAAW;IACpB,OAAO;AACH,YAAM,IAAIQ,MACN,kFAAA;IAER;AACA,SAAKP,cAAcQ,wBAAwBC,mBAAkBZ,UAAKG,gBAALH,YAAoBW,wBAAwBE,oBAAoB;AAC7H,SAAKT,QAAQO,wBAAwBP,MAAM,KAAKD,WAAW;AAC3D,SAAKE,eAAcL,UAAKK,gBAALL,YAAoB;AACvC,SAAKM,OAAMN,UAAKM,QAALN,YAAYc,KAAKR;EAChC;EAEQS,cAA6B;AA1DzC;AA2DQ,QAAI,CAAC,KAAKV,YAAa,QAAOW,QAAQC,QAAO;AAC7C,eAAKV,UAAL,iBAAKA,QAAUI,wBAAwBO,QAAQ,KAAKjB,MAAM,KAAKE,WAAW,EAAEgB,MAAMC,CAAAA,UAAAA;AAC9E,WAAKb,QAAQc;AACb,YAAMD;IACV,CAAA;AACA,WAAO,KAAKb;EAChB;EAEA,MAAMe,KAAKC,KAAaC,UAAuC;AAC3D,UAAM,KAAKT,YAAW;AACtB,UAAM,KAAKd,KAAKwB,MACZ,eAAe,KAAKrB,KAAK;;iHAGzB;MAACmB;MAAKG,KAAKC,UAAUH,QAAAA;MAAW,KAAKlB,IAAG;KAAG;EAEnD;EAEA,MAAMsB,KAAKL,KAAgD;AA7E/D;AA8EQ,UAAM,KAAKR,YAAW;AACtB,UAAMc,MAAM,MAAM,KAAK5B,KAAKwB,MACxB,wBAAwB,KAAKrB,KAAK,sBAClC;MAACmB;KAAI;AAET,YAAOM,SAAIC,KAAK,CAAA,MAATD,mBAAaL;EACxB;EAEA,MAAMO,OAAOR,KAA4B;AACrC,UAAM,KAAKR,YAAW;AACtB,UAAM,KAAKd,KAAKwB,MAAM,eAAe,KAAKrB,KAAK,sBAAsB;MAACmB;KAAI;EAC9E;;;;;;;;EASA,MAAMS,YAAYC,aAAsC;AAlG5D;AAmGQ,UAAM,KAAKlB,YAAW;AACtB,UAAMc,MAAM,MAAM,KAAK5B,KAAKwB,MACxB,eAAe,KAAKrB,KAAK;qCAEzB;MAAC,KAAKE,IAAG,IAAK4B,KAAKC,IAAI,GAAGD,KAAKE,MAAMH,WAAAA,CAAAA;KAAc;AAEvD,YAAOJ,SAAIQ,aAAJR,YAAgB;EAC3B;;EAGA,MAAMS,QAAuB;AACzB,QAAI,KAAKpC,SAAU,OAAM,KAAKD,KAAKsC,IAAG;EAC1C;AACJ;AAjFaxC;AAAN,IAAMA,8BAAN;","names":["Pool","CNSPgBossProgressRepository","opts","pool","ownsPool","tablePrefix","table","autoMigrate","now","ready","connectionString","Pool","Error","CNSPgBossProgressSchema","assertTablePrefix","DEFAULT_TABLE_PREFIX","Date","schemaReady","Promise","resolve","migrate","catch","error","undefined","save","key","progress","query","JSON","stringify","load","res","rows","delete","deleteStale","olderThanMs","Math","max","floor","rowCount","close","end"]}
|