@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
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
retry/backoff/scheduling/singleton, and stream **per-hop progress** into any store for a
|
|
8
8
|
live admin/history view.
|
|
9
9
|
|
|
10
|
-
It follows the same pattern as the [
|
|
10
|
+
It follows the same pattern as the [BullMQ guide](https://cnstra.org/docs/integrations/bullmq):
|
|
11
11
|
**one job = one stimulation**, reconstructed across processes by **name** via a
|
|
12
12
|
`CNSPersistOptionsRegistry` (neuron/collateral object references are not portable).
|
|
13
13
|
|
|
@@ -19,14 +19,18 @@ npm install @cnstra/pg-boss @cnstra/core pg-boss
|
|
|
19
19
|
|
|
20
20
|
## Worker
|
|
21
21
|
|
|
22
|
-
```ts
|
|
23
|
-
import PgBoss from 'pg-boss';
|
|
24
|
-
import {
|
|
22
|
+
```ts file=worker.ts
|
|
23
|
+
import { PgBoss } from 'pg-boss';
|
|
24
|
+
import { cnsCollateral, cnsNeuron } from '@cnstra/core';
|
|
25
|
+
import { CNSFactory } from '@cnstra/factory';
|
|
25
26
|
import { createCNSWorker } from '@cnstra/pg-boss';
|
|
26
27
|
|
|
27
|
-
const
|
|
28
|
+
export const importUser = cnsCollateral<{ userId: string }>();
|
|
29
|
+
const importUserNeuron = cnsNeuron({}).dendrite({ collateral: importUser, response: async () => {} });
|
|
30
|
+
|
|
31
|
+
export const { cns, registry } = CNSFactory.create({ importUserNeuron, /* … */ });
|
|
28
32
|
|
|
29
|
-
const boss = new PgBoss(process.env.DATABASE_URL!);
|
|
33
|
+
export const boss = new PgBoss(process.env.DATABASE_URL!);
|
|
30
34
|
await boss.start();
|
|
31
35
|
await boss.createQueue('cns');
|
|
32
36
|
|
|
@@ -48,13 +52,14 @@ error propagates so **pg-boss applies its retry/backoff policy**.
|
|
|
48
52
|
|
|
49
53
|
## Producer
|
|
50
54
|
|
|
51
|
-
```ts
|
|
52
|
-
import {
|
|
55
|
+
```ts file=producer.ts
|
|
56
|
+
import { enqueueCNSStimulation, cnsStimulationJob } from '@cnstra/pg-boss';
|
|
57
|
+
import { boss, importUser, registry } from './worker';
|
|
53
58
|
|
|
54
59
|
// Type-safe: throws if the collateral isn't registered
|
|
55
|
-
const job =
|
|
60
|
+
const job = cnsStimulationJob(registry, importUser, { userId: '42' });
|
|
56
61
|
|
|
57
|
-
await
|
|
62
|
+
await enqueueCNSStimulation(boss, 'cns', job, {
|
|
58
63
|
retryLimit: 5,
|
|
59
64
|
retryBackoff: true,
|
|
60
65
|
singletonKey: 'user:42', // one in-flight stimulation per entity
|
|
@@ -69,8 +74,15 @@ its context. In pg-boss a run = stimulation = job, so the checkpoint is keyed by
|
|
|
69
74
|
|
|
70
75
|
The checkpoint store is **pluggable** — three ways:
|
|
71
76
|
|
|
77
|
+
```ts setup
|
|
78
|
+
import type { RedisOptions } from 'ioredis';
|
|
79
|
+
|
|
80
|
+
declare const DATABASE_URL: string;
|
|
81
|
+
declare const connection: RedisOptions;
|
|
82
|
+
```
|
|
83
|
+
|
|
72
84
|
```ts
|
|
73
|
-
// 1) In-package Postgres table (cns_pgboss_progress) — opt-in subpath, pulls in `pg`
|
|
85
|
+
// 1) In-package Postgres table (cns_pgboss_progress by default) — opt-in subpath, pulls in `pg`
|
|
74
86
|
import { CNSPgBossProgressRepository } from '@cnstra/pg-boss/postgres-progress';
|
|
75
87
|
resume: { repository: new CNSPgBossProgressRepository({ connectionString: DATABASE_URL }) }
|
|
76
88
|
|
|
@@ -85,8 +97,8 @@ resume: { repository: new CNSRedisProgressRepository({ connection }) }
|
|
|
85
97
|
pg-boss's `Job` has no mutable native progress field (unlike BullMQ), so a checkpoint needs a store.
|
|
86
98
|
|
|
87
99
|
**Migrations:** the Postgres table is owned by this package. Auto-created on first use, or manage it
|
|
88
|
-
yourself: `DATABASE_URL=… npx cnstra-pgboss-progress migrate` (or call `
|
|
89
|
-
`@cnstra/pg-boss/postgres-progress`).
|
|
100
|
+
yourself: `DATABASE_URL=… npx cnstra-pgboss-progress migrate` (or call `CNSPgBossProgressSchema.migrate(pool, tablePrefix)`
|
|
101
|
+
from `@cnstra/pg-boss/postgres-progress`). The table is `<tablePrefix>pgboss_progress(job_id PK, progress jsonb, updated_at)` (`tablePrefix` option, default `cns_`); it holds only jobs in flight, so `deleteStale` scans it without an index.
|
|
90
102
|
|
|
91
103
|
**Row lifecycle / cleanup:** one upserted row per `job.id`. The worker **deletes it when the job
|
|
92
104
|
succeeds**. A job that fails *permanently* (retries exhausted) keeps its row — pg-boss has no
|
|
@@ -94,6 +106,8 @@ terminal-failure hook to key off — so prune those periodically by staleness (a
|
|
|
94
106
|
keeps `updated_at` fresh, so pick a threshold well above your longest retry window):
|
|
95
107
|
|
|
96
108
|
```ts
|
|
109
|
+
import { CNSPgBossProgressRepository } from '@cnstra/pg-boss/postgres-progress';
|
|
110
|
+
|
|
97
111
|
const store = new CNSPgBossProgressRepository({ connectionString: DATABASE_URL });
|
|
98
112
|
// e.g. in a daily cron: drop checkpoints untouched for over a day
|
|
99
113
|
await store.deleteStale(24 * 60 * 60 * 1000);
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__name,
|
|
3
|
+
__publicField
|
|
4
|
+
} from "./chunk-2JTKI4GS.mjs";
|
|
5
|
+
|
|
6
|
+
// ../postgres-schema/dist/index.mjs
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __defNormalProp = /* @__PURE__ */ __name((obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value, "__defNormalProp");
|
|
9
|
+
var __name2 = /* @__PURE__ */ __name((target, value) => __defProp(target, "name", { value, configurable: true }), "__name");
|
|
10
|
+
var __publicField2 = /* @__PURE__ */ __name((obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value), "__publicField");
|
|
11
|
+
var _a;
|
|
12
|
+
var _CNSPostgresSchemaMigrator = (_a = class {
|
|
13
|
+
constructor(options) {
|
|
14
|
+
__publicField2(this, "options");
|
|
15
|
+
__publicField2(this, "steps");
|
|
16
|
+
var _a4;
|
|
17
|
+
this.options = options;
|
|
18
|
+
this.steps = [
|
|
19
|
+
...options.steps
|
|
20
|
+
].sort((left, right) => left.step - right.step);
|
|
21
|
+
this.steps.forEach((step, index) => {
|
|
22
|
+
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}`);
|
|
23
|
+
});
|
|
24
|
+
if (((_a4 = this.steps[0]) == null ? void 0 : _a4.kind) !== "expand") throw new Error(`[${options.name}] schema step 1 must be an expand step`);
|
|
25
|
+
}
|
|
26
|
+
migrationTable(tablePrefix) {
|
|
27
|
+
return `${tablePrefix}${this.options.name}_migration`;
|
|
28
|
+
}
|
|
29
|
+
async migrate(database, tablePrefix, options = {}) {
|
|
30
|
+
await database.transaction(async (connection) => {
|
|
31
|
+
await connection.query("SELECT pg_advisory_xact_lock($1, hashtext($2))", [
|
|
32
|
+
_a.LOCK_NAMESPACE,
|
|
33
|
+
tablePrefix + this.options.name
|
|
34
|
+
]);
|
|
35
|
+
await connection.query(`SET LOCAL lock_timeout = '${_a.LOCK_TIMEOUT}'`);
|
|
36
|
+
const applied = await this.readAppliedSteps(connection, tablePrefix);
|
|
37
|
+
if (applied === void 0) await this.assertNoUntrackedTables(connection, tablePrefix);
|
|
38
|
+
const pending = this.pendingSteps(applied != null ? applied : /* @__PURE__ */ new Map(), applied === void 0 || options.contract === true);
|
|
39
|
+
if (pending.length > 0) await connection.query(this.sqlFor(tablePrefix, pending, applied === void 0));
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async verify(connection, tablePrefix) {
|
|
43
|
+
const applied = await this.readAppliedSteps(connection, tablePrefix);
|
|
44
|
+
if (applied === void 0) {
|
|
45
|
+
throw new Error(`[${this.options.name}] the schema is not installed under prefix "${tablePrefix}": run migrate()`);
|
|
46
|
+
}
|
|
47
|
+
const missing = this.steps.filter((step) => step.kind === "expand" && !applied.has(step.step)).map((step) => step.step);
|
|
48
|
+
if (missing.length > 0) {
|
|
49
|
+
throw new Error(`[${this.options.name}] schema steps ${missing.join(", ")} are not applied under prefix "${tablePrefix}": run migrate()`);
|
|
50
|
+
}
|
|
51
|
+
const unknown = [
|
|
52
|
+
...applied
|
|
53
|
+
].filter(([step, kind]) => kind === "contract" && step > this.steps.length).map(([step]) => step);
|
|
54
|
+
if (unknown.length > 0) {
|
|
55
|
+
throw new Error(`[${this.options.name}] the database has contract steps ${unknown.join(", ")} that this version does not know: upgrade the package`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
migrationSql(tablePrefix, appliedSteps = [], options = {}) {
|
|
59
|
+
const fresh = appliedSteps.length === 0;
|
|
60
|
+
const applied = new Map(appliedSteps.map((step) => [
|
|
61
|
+
step,
|
|
62
|
+
"expand"
|
|
63
|
+
]));
|
|
64
|
+
return this.sqlFor(tablePrefix, this.pendingSteps(applied, fresh || options.contract === true), fresh);
|
|
65
|
+
}
|
|
66
|
+
assertTablePrefix(tablePrefix, longestSuffix) {
|
|
67
|
+
const longest = Math.max(longestSuffix.length, `${this.options.name}_migration`.length);
|
|
68
|
+
const limit = _a.MAX_IDENTIFIER_LENGTH - longest;
|
|
69
|
+
if (!new RegExp(`^[A-Za-z_][A-Za-z0-9_]{0,${limit - 1}}$`).test(tablePrefix)) {
|
|
70
|
+
throw new Error(`[${this.options.name}] unsafe table prefix ${JSON.stringify(tablePrefix)}: letters, digits and _ only, at most ${limit} characters`);
|
|
71
|
+
}
|
|
72
|
+
return tablePrefix;
|
|
73
|
+
}
|
|
74
|
+
pendingSteps(applied, withContract) {
|
|
75
|
+
return this.steps.filter((step) => !applied.has(step.step) && (step.kind === "expand" || withContract));
|
|
76
|
+
}
|
|
77
|
+
sqlFor(tablePrefix, steps, createMigrationTable) {
|
|
78
|
+
const table = this.migrationTable(tablePrefix);
|
|
79
|
+
const parts = createMigrationTable ? [
|
|
80
|
+
`CREATE TABLE ${table} (step integer PRIMARY KEY, kind text NOT NULL CHECK (kind IN ('expand', 'contract')));`
|
|
81
|
+
] : [];
|
|
82
|
+
for (const step of steps) {
|
|
83
|
+
parts.push(step.sql(tablePrefix), `INSERT INTO ${table} (step, kind) VALUES (${step.step}, '${step.kind}');`);
|
|
84
|
+
}
|
|
85
|
+
return parts.join("\n");
|
|
86
|
+
}
|
|
87
|
+
async readAppliedSteps(connection, tablePrefix) {
|
|
88
|
+
var _a4;
|
|
89
|
+
const table = this.migrationTable(tablePrefix);
|
|
90
|
+
const exists = await connection.query("SELECT to_regclass($1)::text AS found", [
|
|
91
|
+
table
|
|
92
|
+
]);
|
|
93
|
+
if (((_a4 = exists.rows[0]) == null ? void 0 : _a4.found) == null) return void 0;
|
|
94
|
+
const rows = await connection.query(`SELECT step, kind FROM ${table}`);
|
|
95
|
+
return new Map(rows.rows.map((row) => [
|
|
96
|
+
Number(row.step),
|
|
97
|
+
row.kind
|
|
98
|
+
]));
|
|
99
|
+
}
|
|
100
|
+
async assertNoUntrackedTables(connection, tablePrefix) {
|
|
101
|
+
const tables = this.options.tables(tablePrefix);
|
|
102
|
+
const found = await connection.query("SELECT name FROM unnest($1::text[]) AS name WHERE to_regclass(name) IS NOT NULL", [
|
|
103
|
+
tables
|
|
104
|
+
]);
|
|
105
|
+
if (found.rows.length > 0) {
|
|
106
|
+
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()`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}, __name(_a, "_CNSPostgresSchemaMigrator"), _a);
|
|
110
|
+
__name2(_CNSPostgresSchemaMigrator, "CNSPostgresSchemaMigrator");
|
|
111
|
+
__publicField2(_CNSPostgresSchemaMigrator, "LOCK_NAMESPACE", 6);
|
|
112
|
+
__publicField2(_CNSPostgresSchemaMigrator, "LOCK_TIMEOUT", "5s");
|
|
113
|
+
__publicField2(_CNSPostgresSchemaMigrator, "MAX_IDENTIFIER_LENGTH", 63);
|
|
114
|
+
var CNSPostgresSchemaMigrator = _CNSPostgresSchemaMigrator;
|
|
115
|
+
var _a2;
|
|
116
|
+
var _CNSPostgresSchemaPgDatabase = (_a2 = class {
|
|
117
|
+
constructor(open) {
|
|
118
|
+
__publicField2(this, "open");
|
|
119
|
+
this.open = open;
|
|
120
|
+
}
|
|
121
|
+
static fromPool(pool) {
|
|
122
|
+
return new _a2(async () => {
|
|
123
|
+
const client = await pool.connect();
|
|
124
|
+
return {
|
|
125
|
+
connection: client,
|
|
126
|
+
release: /* @__PURE__ */ __name2((error) => client.release(error), "release")
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async transaction(work) {
|
|
131
|
+
const { connection, release } = await this.open();
|
|
132
|
+
try {
|
|
133
|
+
await connection.query("BEGIN");
|
|
134
|
+
const result = await work(connection);
|
|
135
|
+
await connection.query("COMMIT");
|
|
136
|
+
release();
|
|
137
|
+
return result;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const rollback = await connection.query("ROLLBACK").then(() => void 0, (rollbackError) => rollbackError);
|
|
140
|
+
release(rollback instanceof Error ? rollback : void 0);
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}, __name(_a2, "_CNSPostgresSchemaPgDatabase"), _a2);
|
|
145
|
+
__name2(_CNSPostgresSchemaPgDatabase, "CNSPostgresSchemaPgDatabase");
|
|
146
|
+
var CNSPostgresSchemaPgDatabase = _CNSPostgresSchemaPgDatabase;
|
|
147
|
+
var _a3;
|
|
148
|
+
var _CNSPostgresSchemaPartitionRotation = (_a3 = class {
|
|
149
|
+
constructor(options) {
|
|
150
|
+
__publicField2(this, "options");
|
|
151
|
+
__publicField2(this, "rotatedFor", /* @__PURE__ */ new Map());
|
|
152
|
+
this.options = options;
|
|
153
|
+
if (!(options.premakeDays >= 1) || !Number.isInteger(options.premakeDays)) {
|
|
154
|
+
throw new Error(`[partition rotation] premakeDays must be a positive integer, got ${options.premakeDays}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async rotate(database, table, now, dropBefore, beforeDrop) {
|
|
158
|
+
const today = Math.floor(now / _a3.DAY_MS);
|
|
159
|
+
const days = `${today}:${Math.floor(dropBefore / _a3.DAY_MS)}`;
|
|
160
|
+
if (this.rotatedFor.get(table) === days) return [];
|
|
161
|
+
const dropped = await this.underLock(database, table, async (connection) => {
|
|
162
|
+
const expired = (await this.readRangePartitions(connection, table)).filter((partition) => partition.to <= dropBefore);
|
|
163
|
+
if (beforeDrop) for (const partition of expired) await beforeDrop(connection, partition.name);
|
|
164
|
+
for (const partition of expired) await connection.query(`DROP TABLE ${partition.name}`);
|
|
165
|
+
return expired.map((partition) => partition.name);
|
|
166
|
+
});
|
|
167
|
+
await this.underLock(database, table, async (connection) => {
|
|
168
|
+
const partitions = await this.readRangePartitions(connection, table);
|
|
169
|
+
for (let ahead = 1; ahead <= this.options.premakeDays; ahead++) {
|
|
170
|
+
const from = (today + ahead) * _a3.DAY_MS;
|
|
171
|
+
const to = from + _a3.DAY_MS;
|
|
172
|
+
if (partitions.some((partition) => partition.from < to && from < partition.to)) continue;
|
|
173
|
+
const name = `${table}_p${new Date(from).toISOString().slice(0, 10).replace(/-/g, "")}`;
|
|
174
|
+
await connection.query(`CREATE TABLE ${name} (LIKE ${table} INCLUDING DEFAULTS INCLUDING CONSTRAINTS)`);
|
|
175
|
+
await connection.query(`ALTER TABLE ${table} ATTACH PARTITION ${name} FOR VALUES FROM (${from}) TO (${to})`);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
this.rotatedFor.set(table, days);
|
|
179
|
+
return dropped;
|
|
180
|
+
}
|
|
181
|
+
underLock(database, table, work) {
|
|
182
|
+
return database.transaction(async (connection) => {
|
|
183
|
+
await connection.query("SELECT pg_advisory_xact_lock($1, hashtext($2))", [
|
|
184
|
+
_a3.LOCK_NAMESPACE,
|
|
185
|
+
table
|
|
186
|
+
]);
|
|
187
|
+
await connection.query(`SET LOCAL lock_timeout = '${_a3.LOCK_TIMEOUT}'`);
|
|
188
|
+
return work(connection);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
async readRangePartitions(connection, table) {
|
|
192
|
+
const rows = await connection.query(`SELECT child.oid::regclass::text AS name, pg_get_expr(child.relpartbound, child.oid) AS bound
|
|
193
|
+
FROM pg_inherits
|
|
194
|
+
JOIN pg_class child ON child.oid = pg_inherits.inhrelid
|
|
195
|
+
WHERE pg_inherits.inhparent = $1::regclass`, [
|
|
196
|
+
table
|
|
197
|
+
]);
|
|
198
|
+
const partitions = [];
|
|
199
|
+
for (const row of rows.rows) {
|
|
200
|
+
const range = /^FOR VALUES FROM \('?(-?[\d.e+]+)'?\) TO \('?(-?[\d.e+]+)'?\)$/.exec(row.bound);
|
|
201
|
+
if (range) partitions.push({
|
|
202
|
+
name: row.name,
|
|
203
|
+
from: Number(range[1]),
|
|
204
|
+
to: Number(range[2])
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return partitions;
|
|
208
|
+
}
|
|
209
|
+
}, __name(_a3, "_CNSPostgresSchemaPartitionRotation"), _a3);
|
|
210
|
+
__name2(_CNSPostgresSchemaPartitionRotation, "CNSPostgresSchemaPartitionRotation");
|
|
211
|
+
__publicField2(_CNSPostgresSchemaPartitionRotation, "LOCK_NAMESPACE", 8);
|
|
212
|
+
__publicField2(_CNSPostgresSchemaPartitionRotation, "LOCK_TIMEOUT", "5s");
|
|
213
|
+
__publicField2(_CNSPostgresSchemaPartitionRotation, "DAY_MS", 24 * 60 * 60 * 1e3);
|
|
214
|
+
|
|
215
|
+
// src/schema/CNSPgBossProgressSchema.ts
|
|
216
|
+
var _CNSPgBossProgressSchema = class _CNSPgBossProgressSchema {
|
|
217
|
+
static assertTablePrefix(tablePrefix) {
|
|
218
|
+
return _CNSPgBossProgressSchema.migrator.assertTablePrefix(tablePrefix, _CNSPgBossProgressSchema.LONGEST_SUFFIX);
|
|
219
|
+
}
|
|
220
|
+
static table(tablePrefix = _CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX) {
|
|
221
|
+
return `${_CNSPgBossProgressSchema.assertTablePrefix(tablePrefix)}pgboss_progress`;
|
|
222
|
+
}
|
|
223
|
+
static migrationTable(tablePrefix = _CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX) {
|
|
224
|
+
return _CNSPgBossProgressSchema.migrator.migrationTable(_CNSPgBossProgressSchema.assertTablePrefix(tablePrefix));
|
|
225
|
+
}
|
|
226
|
+
static migrationSql(tablePrefix = _CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX, appliedSteps = [], options) {
|
|
227
|
+
return _CNSPgBossProgressSchema.migrator.migrationSql(_CNSPgBossProgressSchema.assertTablePrefix(tablePrefix), appliedSteps, options);
|
|
228
|
+
}
|
|
229
|
+
static async migrate(pool, tablePrefix = _CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX, options) {
|
|
230
|
+
await _CNSPgBossProgressSchema.migrator.migrate(CNSPostgresSchemaPgDatabase.fromPool(pool), _CNSPgBossProgressSchema.assertTablePrefix(tablePrefix), options);
|
|
231
|
+
}
|
|
232
|
+
static async verify(pool, tablePrefix = _CNSPgBossProgressSchema.DEFAULT_TABLE_PREFIX) {
|
|
233
|
+
await _CNSPgBossProgressSchema.migrator.verify(pool, _CNSPgBossProgressSchema.assertTablePrefix(tablePrefix));
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
__name(_CNSPgBossProgressSchema, "CNSPgBossProgressSchema");
|
|
237
|
+
__publicField(_CNSPgBossProgressSchema, "DEFAULT_TABLE_PREFIX", "cns_");
|
|
238
|
+
__publicField(_CNSPgBossProgressSchema, "LONGEST_SUFFIX", "pgboss_progress_pkey");
|
|
239
|
+
__publicField(_CNSPgBossProgressSchema, "migrator", new CNSPostgresSchemaMigrator({
|
|
240
|
+
name: "pgboss_progress",
|
|
241
|
+
tables: /* @__PURE__ */ __name((tablePrefix) => [
|
|
242
|
+
_CNSPgBossProgressSchema.table(tablePrefix)
|
|
243
|
+
], "tables"),
|
|
244
|
+
steps: [
|
|
245
|
+
{
|
|
246
|
+
step: 1,
|
|
247
|
+
kind: "expand",
|
|
248
|
+
sql: /* @__PURE__ */ __name((p) => `
|
|
249
|
+
CREATE TABLE ${p}pgboss_progress (
|
|
250
|
+
job_id text PRIMARY KEY,
|
|
251
|
+
progress jsonb NOT NULL,
|
|
252
|
+
updated_at bigint NOT NULL
|
|
253
|
+
);
|
|
254
|
+
`, "sql")
|
|
255
|
+
}
|
|
256
|
+
]
|
|
257
|
+
}));
|
|
258
|
+
var CNSPgBossProgressSchema = _CNSPgBossProgressSchema;
|
|
259
|
+
|
|
260
|
+
export {
|
|
261
|
+
CNSPgBossProgressSchema
|
|
262
|
+
};
|
|
263
|
+
//# sourceMappingURL=chunk-OJUUT4VL.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../postgres-schema/src/CNSPostgresSchemaMigrator.ts","../../postgres-schema/src/CNSPostgresSchemaPgDatabase.ts","../../postgres-schema/src/CNSPostgresSchemaPartitionRotation.ts","../src/schema/CNSPgBossProgressSchema.ts"],"sourcesContent":["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"]}
|