@relaybus/postgres 0.3.1 → 0.4.1
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 +46 -24
- package/dist/drizzle.d.cts +3 -2
- package/dist/drizzle.d.ts +3 -2
- package/dist/index.cjs +155 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -6
- package/dist/index.d.ts +20 -6
- package/dist/index.js +155 -44
- package/dist/index.js.map +1 -1
- package/dist/knex.d.cts +3 -2
- package/dist/knex.d.ts +3 -2
- package/dist/kysely.d.cts +3 -2
- package/dist/kysely.d.ts +3 -2
- package/dist/prisma.cjs.map +1 -1
- package/dist/prisma.d.cts +4 -4
- package/dist/prisma.d.ts +4 -4
- package/dist/prisma.js.map +1 -1
- package/dist/typeorm.d.cts +3 -2
- package/dist/typeorm.d.ts +3 -2
- package/package.json +20 -12
package/dist/index.d.cts
CHANGED
|
@@ -70,6 +70,8 @@ interface PostgresNames {
|
|
|
70
70
|
readonly schema: string;
|
|
71
71
|
readonly quotedSchema: string;
|
|
72
72
|
readonly events: string;
|
|
73
|
+
/** Unquoted, unqualified name of {@link events}, for catalog lookups. */
|
|
74
|
+
readonly eventsTableName: string;
|
|
73
75
|
readonly deliveries: string;
|
|
74
76
|
readonly keySequences: string;
|
|
75
77
|
/** Lane lease rows: one per lane, see {@link LANE_COUNT}. */
|
|
@@ -110,8 +112,8 @@ declare function resolvePostgresNames(options?: PostgresSchemaOptions): Postgres
|
|
|
110
112
|
|
|
111
113
|
/**
|
|
112
114
|
* Statements that create the schema, tables and indexes, in dependency order,
|
|
113
|
-
* and upgrade a 0.1.0 schema
|
|
114
|
-
* (`IF NOT EXISTS`, or guarded by a check for the
|
|
115
|
+
* and upgrade a 0.1.0 schema and the `jsonb` payload columns of schemas before 0.4.1 in place.
|
|
116
|
+
* Every statement is idempotent (`IF NOT EXISTS`, or guarded by a check for the old columns). Run them in
|
|
115
117
|
* one transaction and serialize concurrent runs (see
|
|
116
118
|
* {@link PostgresEventStore.setup}), or paste {@link createPostgresSchemaSql}
|
|
117
119
|
* into your own migration tool.
|
|
@@ -190,7 +192,18 @@ interface PostgresConnectionOptions extends PostgresEventStoreSettings {
|
|
|
190
192
|
/** Use another database library through an adapter. The store never closes it. */
|
|
191
193
|
interface PostgresExecutorOptions<TTransaction> extends PostgresEventStoreSettings {
|
|
192
194
|
readonly executor: PostgresExecutor<TTransaction>;
|
|
193
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Optional `pg` pool on the same database, for example the one an ORM's `pg` driver adapter uses
|
|
197
|
+
* (`new PrismaPg(pool)`, Kysely's `PostgresDialect`, Drizzle's `node-postgres`). The store never
|
|
198
|
+
* closes it. With it:
|
|
199
|
+
* - the statements the store sends outside a transaction (the dispatcher's claims, completions and
|
|
200
|
+
* lease renewals, maintenance) run on it as prepared statements (`preparedStatements`), which ORM
|
|
201
|
+
* raw queries cannot do; statements in a transaction still go through `executor`;
|
|
202
|
+
* - with `wakeUp: 'notify'` the store listens for wake-ups with its connection settings, so a
|
|
203
|
+
* delivery published in an ORM transaction is dispatched right after that transaction commits,
|
|
204
|
+
* instead of after a fixed delay or the next poll.
|
|
205
|
+
*/
|
|
206
|
+
readonly pool?: Pool;
|
|
194
207
|
readonly connectionString?: never;
|
|
195
208
|
readonly poolConfig?: never;
|
|
196
209
|
}
|
|
@@ -239,8 +252,8 @@ declare class PostgresEventStore<TTransaction = PoolClient> implements EventStor
|
|
|
239
252
|
* Creates the schema, tables and indexes if they do not exist. Safe to call
|
|
240
253
|
* from many processes at once: a transaction-scoped advisory lock serializes
|
|
241
254
|
* the DDL, so racing `CREATE TABLE IF NOT EXISTS` statements cannot collide.
|
|
242
|
-
* When the lane table already carries the marker
|
|
243
|
-
*
|
|
255
|
+
* When the lane table already carries the schema marker ({@link version040SchemaMarker})
|
|
256
|
+
* and the payload columns are `json`, nothing runs and no table is locked, so instances
|
|
244
257
|
* that start next to running dispatchers cannot deadlock with their claims.
|
|
245
258
|
* Skip it (`setupStore: false`) when a migration tool applies
|
|
246
259
|
* `createPostgresSchemaSql()` instead.
|
|
@@ -330,4 +343,5 @@ declare class ReconcileFailedError extends RelayBusError {
|
|
|
330
343
|
readonly code: string;
|
|
331
344
|
}
|
|
332
345
|
|
|
333
|
-
export { PgExecutor,
|
|
346
|
+
export { PgExecutor, PostgresEventStore, PostgresExecutor, PostgresParameter, PostgresQueryOptions, PostgresRow, ReconcileFailedError, createPostgresSchemaSql, createPostgresSchemaStatements, resolvePostgresNames };
|
|
347
|
+
export type { PgExecutorOptions, PostgresConnectionOptions, PostgresEventStoreOptions, PostgresEventStoreSettings, PostgresExecutorOptions, PostgresNames, PostgresPoolOptions, PostgresSchemaOptions, PostgresWakeUp };
|
package/dist/index.d.ts
CHANGED
|
@@ -70,6 +70,8 @@ interface PostgresNames {
|
|
|
70
70
|
readonly schema: string;
|
|
71
71
|
readonly quotedSchema: string;
|
|
72
72
|
readonly events: string;
|
|
73
|
+
/** Unquoted, unqualified name of {@link events}, for catalog lookups. */
|
|
74
|
+
readonly eventsTableName: string;
|
|
73
75
|
readonly deliveries: string;
|
|
74
76
|
readonly keySequences: string;
|
|
75
77
|
/** Lane lease rows: one per lane, see {@link LANE_COUNT}. */
|
|
@@ -110,8 +112,8 @@ declare function resolvePostgresNames(options?: PostgresSchemaOptions): Postgres
|
|
|
110
112
|
|
|
111
113
|
/**
|
|
112
114
|
* Statements that create the schema, tables and indexes, in dependency order,
|
|
113
|
-
* and upgrade a 0.1.0 schema
|
|
114
|
-
* (`IF NOT EXISTS`, or guarded by a check for the
|
|
115
|
+
* and upgrade a 0.1.0 schema and the `jsonb` payload columns of schemas before 0.4.1 in place.
|
|
116
|
+
* Every statement is idempotent (`IF NOT EXISTS`, or guarded by a check for the old columns). Run them in
|
|
115
117
|
* one transaction and serialize concurrent runs (see
|
|
116
118
|
* {@link PostgresEventStore.setup}), or paste {@link createPostgresSchemaSql}
|
|
117
119
|
* into your own migration tool.
|
|
@@ -190,7 +192,18 @@ interface PostgresConnectionOptions extends PostgresEventStoreSettings {
|
|
|
190
192
|
/** Use another database library through an adapter. The store never closes it. */
|
|
191
193
|
interface PostgresExecutorOptions<TTransaction> extends PostgresEventStoreSettings {
|
|
192
194
|
readonly executor: PostgresExecutor<TTransaction>;
|
|
193
|
-
|
|
195
|
+
/**
|
|
196
|
+
* Optional `pg` pool on the same database, for example the one an ORM's `pg` driver adapter uses
|
|
197
|
+
* (`new PrismaPg(pool)`, Kysely's `PostgresDialect`, Drizzle's `node-postgres`). The store never
|
|
198
|
+
* closes it. With it:
|
|
199
|
+
* - the statements the store sends outside a transaction (the dispatcher's claims, completions and
|
|
200
|
+
* lease renewals, maintenance) run on it as prepared statements (`preparedStatements`), which ORM
|
|
201
|
+
* raw queries cannot do; statements in a transaction still go through `executor`;
|
|
202
|
+
* - with `wakeUp: 'notify'` the store listens for wake-ups with its connection settings, so a
|
|
203
|
+
* delivery published in an ORM transaction is dispatched right after that transaction commits,
|
|
204
|
+
* instead of after a fixed delay or the next poll.
|
|
205
|
+
*/
|
|
206
|
+
readonly pool?: Pool;
|
|
194
207
|
readonly connectionString?: never;
|
|
195
208
|
readonly poolConfig?: never;
|
|
196
209
|
}
|
|
@@ -239,8 +252,8 @@ declare class PostgresEventStore<TTransaction = PoolClient> implements EventStor
|
|
|
239
252
|
* Creates the schema, tables and indexes if they do not exist. Safe to call
|
|
240
253
|
* from many processes at once: a transaction-scoped advisory lock serializes
|
|
241
254
|
* the DDL, so racing `CREATE TABLE IF NOT EXISTS` statements cannot collide.
|
|
242
|
-
* When the lane table already carries the marker
|
|
243
|
-
*
|
|
255
|
+
* When the lane table already carries the schema marker ({@link version040SchemaMarker})
|
|
256
|
+
* and the payload columns are `json`, nothing runs and no table is locked, so instances
|
|
244
257
|
* that start next to running dispatchers cannot deadlock with their claims.
|
|
245
258
|
* Skip it (`setupStore: false`) when a migration tool applies
|
|
246
259
|
* `createPostgresSchemaSql()` instead.
|
|
@@ -330,4 +343,5 @@ declare class ReconcileFailedError extends RelayBusError {
|
|
|
330
343
|
readonly code: string;
|
|
331
344
|
}
|
|
332
345
|
|
|
333
|
-
export { PgExecutor,
|
|
346
|
+
export { PgExecutor, PostgresEventStore, PostgresExecutor, PostgresParameter, PostgresQueryOptions, PostgresRow, ReconcileFailedError, createPostgresSchemaSql, createPostgresSchemaStatements, resolvePostgresNames };
|
|
347
|
+
export type { PgExecutorOptions, PostgresConnectionOptions, PostgresEventStoreOptions, PostgresEventStoreSettings, PostgresExecutorOptions, PostgresNames, PostgresPoolOptions, PostgresSchemaOptions, PostgresWakeUp };
|
package/dist/index.js
CHANGED
|
@@ -228,6 +228,7 @@ function resolvePostgresNames(options = {}) {
|
|
|
228
228
|
schema,
|
|
229
229
|
quotedSchema,
|
|
230
230
|
events: table("events"),
|
|
231
|
+
eventsTableName: name("events"),
|
|
231
232
|
deliveries: table("deliveries"),
|
|
232
233
|
keySequences: table("key_sequences"),
|
|
233
234
|
lanes: table("lanes"),
|
|
@@ -254,8 +255,17 @@ function laneExpression(keyExpression, eventIdExpression) {
|
|
|
254
255
|
|
|
255
256
|
// src/schema.ts
|
|
256
257
|
var SCHEMA_MARKER_DIGITS = 32;
|
|
258
|
+
var PAYLOAD_COLUMNS = ` payload json NOT NULL,
|
|
259
|
+
headers json NOT NULL DEFAULT '{}'::json,`;
|
|
260
|
+
var JSONB_PAYLOAD_COLUMNS = ` payload jsonb NOT NULL,
|
|
261
|
+
headers jsonb NOT NULL DEFAULT '{}'::jsonb,`;
|
|
257
262
|
function createPostgresSchemaStatements(options = {}) {
|
|
258
263
|
const names = resolvePostgresNames(options);
|
|
264
|
+
const statements = schemaBodyStatements(names);
|
|
265
|
+
statements.push(`COMMENT ON TABLE ${names.lanes} IS '${version040Marker(names, statements)}'`);
|
|
266
|
+
return statements;
|
|
267
|
+
}
|
|
268
|
+
function schemaBodyStatements(names) {
|
|
259
269
|
const statements = [...eventAndDeliveryStatements(names), ...laneAndCompletionStatements(names)];
|
|
260
270
|
if (names.completionHistory) {
|
|
261
271
|
statements.push(...completionHistoryStatements(names));
|
|
@@ -263,7 +273,6 @@ function createPostgresSchemaStatements(options = {}) {
|
|
|
263
273
|
if (names.schema !== "public") {
|
|
264
274
|
statements.unshift(`CREATE SCHEMA IF NOT EXISTS ${names.quotedSchema}`);
|
|
265
275
|
}
|
|
266
|
-
statements.push(`COMMENT ON TABLE ${names.lanes} IS '${postgresSchemaMarker(statements)}'`);
|
|
267
276
|
return statements;
|
|
268
277
|
}
|
|
269
278
|
function eventAndDeliveryStatements(names) {
|
|
@@ -277,14 +286,14 @@ function eventAndKeySequenceStatements(names) {
|
|
|
277
286
|
source text NOT NULL,
|
|
278
287
|
key text,
|
|
279
288
|
sequence bigint,
|
|
280
|
-
|
|
281
|
-
headers jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
289
|
+
${PAYLOAD_COLUMNS}
|
|
282
290
|
correlation_id text,
|
|
283
291
|
causation_id text,
|
|
284
292
|
occurred_at timestamptz NOT NULL,
|
|
285
293
|
created_at timestamptz NOT NULL,
|
|
286
294
|
handlers text[] NOT NULL DEFAULT '{}'
|
|
287
295
|
)`,
|
|
296
|
+
upgradePayloadToJson(names),
|
|
288
297
|
`CREATE INDEX IF NOT EXISTS ${names.indexes.eventCreated} ON ${names.events} (created_at, id)`,
|
|
289
298
|
`CREATE TABLE IF NOT EXISTS ${names.keySequences} (
|
|
290
299
|
key text PRIMARY KEY,
|
|
@@ -380,6 +389,33 @@ BEGIN
|
|
|
380
389
|
END
|
|
381
390
|
$relaybus_upgrade$`;
|
|
382
391
|
}
|
|
392
|
+
function upgradePayloadToJson(names) {
|
|
393
|
+
return `DO $relaybus_payload_json$
|
|
394
|
+
BEGIN
|
|
395
|
+
IF EXISTS (
|
|
396
|
+
SELECT 1 FROM pg_attribute
|
|
397
|
+
WHERE attrelid = to_regclass('${names.events}') AND attname IN ('payload', 'headers')
|
|
398
|
+
AND atttypid = 'jsonb'::regtype AND NOT attisdropped
|
|
399
|
+
) THEN
|
|
400
|
+
ALTER TABLE ${names.events}
|
|
401
|
+
ALTER COLUMN headers DROP DEFAULT,
|
|
402
|
+
ALTER COLUMN payload TYPE json USING payload::json,
|
|
403
|
+
ALTER COLUMN headers TYPE json USING headers::json,
|
|
404
|
+
ALTER COLUMN headers SET DEFAULT '{}'::json;
|
|
405
|
+
END IF;
|
|
406
|
+
END
|
|
407
|
+
$relaybus_payload_json$`;
|
|
408
|
+
}
|
|
409
|
+
function version040SchemaMarker(options = {}) {
|
|
410
|
+
const names = resolvePostgresNames(options);
|
|
411
|
+
return version040Marker(names, schemaBodyStatements(names));
|
|
412
|
+
}
|
|
413
|
+
function version040Marker(names, statements) {
|
|
414
|
+
const conversion = upgradePayloadToJson(names);
|
|
415
|
+
return postgresSchemaMarker(
|
|
416
|
+
statements.filter((statement) => statement !== conversion).map((statement) => statement.replace(PAYLOAD_COLUMNS, JSONB_PAYLOAD_COLUMNS))
|
|
417
|
+
);
|
|
418
|
+
}
|
|
383
419
|
function createPostgresSchemaSql(options = {}) {
|
|
384
420
|
return `${createPostgresSchemaStatements(options).join(";\n\n")};
|
|
385
421
|
`;
|
|
@@ -491,6 +527,19 @@ var NotificationListener = class {
|
|
|
491
527
|
await drop?.();
|
|
492
528
|
}
|
|
493
529
|
};
|
|
530
|
+
|
|
531
|
+
// src/root-pool-executor.ts
|
|
532
|
+
function withRootPool(executor, root) {
|
|
533
|
+
return {
|
|
534
|
+
...executor.sessionTimeouts === void 0 ? {} : { sessionTimeouts: executor.sessionTimeouts },
|
|
535
|
+
transaction: (work) => executor.transaction(work),
|
|
536
|
+
...executor.transactionWithBudget === void 0 ? {} : {
|
|
537
|
+
transactionWithBudget: (work, timeoutMs) => executor.transactionWithBudget(work, timeoutMs)
|
|
538
|
+
},
|
|
539
|
+
// eslint-disable-next-line relaybus/max-two-params -- PostgresExecutor port signature
|
|
540
|
+
query: (sql, parameters, transaction, options) => transaction === void 0 ? root.query(sql, parameters, void 0, options) : executor.query(sql, parameters, transaction, options)
|
|
541
|
+
};
|
|
542
|
+
}
|
|
494
543
|
function text(row, column) {
|
|
495
544
|
return row[column];
|
|
496
545
|
}
|
|
@@ -1431,10 +1480,71 @@ var PostgresMaintenance = class {
|
|
|
1431
1480
|
});
|
|
1432
1481
|
}
|
|
1433
1482
|
};
|
|
1483
|
+
|
|
1484
|
+
// src/postgres-setup.ts
|
|
1485
|
+
var PostgresSetup = class {
|
|
1486
|
+
#state;
|
|
1487
|
+
/** The conversion of `jsonb` payload columns. */
|
|
1488
|
+
#payloadUpgrade;
|
|
1489
|
+
constructor(state, options) {
|
|
1490
|
+
this.#state = state;
|
|
1491
|
+
this.#payloadUpgrade = upgradePayloadToJson(resolvePostgresNames(options));
|
|
1492
|
+
}
|
|
1493
|
+
/** Implements {@link PostgresEventStore.setup}. */
|
|
1494
|
+
async run() {
|
|
1495
|
+
if (isCurrent(await this.#schemaState())) {
|
|
1496
|
+
return;
|
|
1497
|
+
}
|
|
1498
|
+
await this.#underSetupLock(async (transaction) => {
|
|
1499
|
+
await this.#run(this.#payloadUpgrade, transaction);
|
|
1500
|
+
});
|
|
1501
|
+
await this.#underSetupLock(async (transaction, schema) => {
|
|
1502
|
+
if (schema.marked) {
|
|
1503
|
+
await this.#run(this.#payloadUpgrade, transaction);
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
for (const statement of this.#state.schemaStatements) {
|
|
1507
|
+
await this.#run(statement, transaction);
|
|
1508
|
+
}
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* Runs `work` in a transaction under the setup lock, unless the schema became current. The
|
|
1513
|
+
* payload conversion goes first in a transaction of its own, so it holds no other table lock
|
|
1514
|
+
* while it waits for the event table (a completion locks deliveries before the event rows).
|
|
1515
|
+
*/
|
|
1516
|
+
async #underSetupLock(work) {
|
|
1517
|
+
await this.#state.executor.transaction(async (transaction) => {
|
|
1518
|
+
await this.#state.executor.query(
|
|
1519
|
+
this.#state.statements.setup.setupLock,
|
|
1520
|
+
[this.#state.setupLockKey],
|
|
1521
|
+
transaction
|
|
1522
|
+
);
|
|
1523
|
+
const schema = await this.#schemaState(transaction);
|
|
1524
|
+
if (!isCurrent(schema)) {
|
|
1525
|
+
await work(transaction, schema);
|
|
1526
|
+
}
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
async #run(statement, transaction) {
|
|
1530
|
+
await this.#state.executor.query(statement, [], transaction);
|
|
1531
|
+
}
|
|
1532
|
+
async #schemaState(transaction) {
|
|
1533
|
+
const rows = await this.#state.executor.query(
|
|
1534
|
+
this.#state.statements.setup.schemaCurrent,
|
|
1535
|
+
[this.#state.schemaMarker],
|
|
1536
|
+
transaction
|
|
1537
|
+
);
|
|
1538
|
+
return { marked: rows[0]["marked"] === true, payloadJson: rows[0]["payload_json"] === true };
|
|
1539
|
+
}
|
|
1540
|
+
};
|
|
1541
|
+
function isCurrent(schema) {
|
|
1542
|
+
return schema.marked && schema.payloadJson;
|
|
1543
|
+
}
|
|
1434
1544
|
var WAKE_UPS = ["notify", "poll"];
|
|
1435
1545
|
var DEFAULT_PURGE_BATCH_SIZE = 1e3;
|
|
1436
1546
|
function resolveStoreSettings(options) {
|
|
1437
|
-
const sources = [options.
|
|
1547
|
+
const sources = [options.executor ?? options.pool, options.connectionString].filter(
|
|
1438
1548
|
(source) => source !== void 0
|
|
1439
1549
|
);
|
|
1440
1550
|
if (sources.length !== 1) {
|
|
@@ -1505,8 +1615,8 @@ WHERE (SELECT count(*) FROM woken) >= 0`;
|
|
|
1505
1615
|
reserved.sequence - count(*) OVER (PARTITION BY incoming.key)
|
|
1506
1616
|
+ row_number() OVER (PARTITION BY incoming.key ORDER BY incoming.ordinal)
|
|
1507
1617
|
END AS sequence
|
|
1508
|
-
FROM
|
|
1509
|
-
ordinal int, id text, type text, source text, key text, payload
|
|
1618
|
+
FROM json_to_recordset($1::json) AS incoming (
|
|
1619
|
+
ordinal int, id text, type text, source text, key text, payload json, headers json,
|
|
1510
1620
|
correlation_id text, causation_id text, occurred_at timestamptz, handlers text[])
|
|
1511
1621
|
LEFT JOIN reserved ON reserved.key = incoming.key
|
|
1512
1622
|
)`;
|
|
@@ -1517,7 +1627,7 @@ WHERE (SELECT count(*) FROM woken) >= 0`;
|
|
|
1517
1627
|
INSERT INTO ${this.#names.events}
|
|
1518
1628
|
(id, type, source, key, sequence, payload, headers, correlation_id, causation_id, occurred_at, created_at, handlers)
|
|
1519
1629
|
SELECT incoming.id, incoming.type, incoming.source, incoming.key, incoming.sequence,
|
|
1520
|
-
COALESCE(incoming.payload, 'null'::
|
|
1630
|
+
COALESCE(incoming.payload, 'null'::json), COALESCE(incoming.headers, '{}'::json),
|
|
1521
1631
|
incoming.correlation_id, incoming.causation_id, incoming.occurred_at, $2::timestamptz,
|
|
1522
1632
|
COALESCE(incoming.handlers, '{}')
|
|
1523
1633
|
FROM incoming
|
|
@@ -1540,9 +1650,13 @@ WHERE (SELECT count(*) FROM woken) >= 0`;
|
|
|
1540
1650
|
RETURNING available_at
|
|
1541
1651
|
)`;
|
|
1542
1652
|
}
|
|
1543
|
-
/**
|
|
1653
|
+
/**
|
|
1654
|
+
* One wake-up notification on {@link PostgresNames.notificationChannel}, outside any publishing
|
|
1655
|
+
* transaction. Returns a boolean column rather than the `void` of `pg_notify`, which some libraries
|
|
1656
|
+
* cannot read (Prisma's raw queries reject a `void` column).
|
|
1657
|
+
*/
|
|
1544
1658
|
get notify() {
|
|
1545
|
-
return `SELECT pg_notify('${this.#names.notificationChannel}', '')`;
|
|
1659
|
+
return `SELECT true AS notified FROM pg_notify('${this.#names.notificationChannel}', '')`;
|
|
1546
1660
|
}
|
|
1547
1661
|
/**
|
|
1548
1662
|
* A CTE `woken` that sends one notification on {@link PostgresNames.notificationChannel} when a row of
|
|
@@ -1566,11 +1680,11 @@ WHERE (SELECT count(*) FROM woken) >= 0`;
|
|
|
1566
1680
|
return `INSERT INTO ${this.#names.events}
|
|
1567
1681
|
(id, type, source, key, sequence, payload, headers, correlation_id, causation_id, occurred_at, created_at, handlers)
|
|
1568
1682
|
SELECT incoming.id, incoming.type, incoming.source, incoming.key, incoming.sequence,
|
|
1569
|
-
COALESCE(incoming.payload, 'null'::
|
|
1683
|
+
COALESCE(incoming.payload, 'null'::json), COALESCE(incoming.headers, '{}'::json),
|
|
1570
1684
|
incoming.correlation_id, incoming.causation_id, incoming.occurred_at, $2::timestamptz,
|
|
1571
1685
|
COALESCE(incoming.handlers, '{}')
|
|
1572
|
-
FROM
|
|
1573
|
-
id text, type text, source text, key text, sequence bigint, payload
|
|
1686
|
+
FROM json_to_recordset($1::json) AS incoming (
|
|
1687
|
+
id text, type text, source text, key text, sequence bigint, payload json, headers json,
|
|
1574
1688
|
correlation_id text, causation_id text, occurred_at timestamptz, handlers text[])
|
|
1575
1689
|
ORDER BY incoming.id
|
|
1576
1690
|
ON CONFLICT (id) DO NOTHING
|
|
@@ -2820,8 +2934,10 @@ var SetupStatements = class {
|
|
|
2820
2934
|
return `relaybus:${this.#names.schema}`;
|
|
2821
2935
|
}
|
|
2822
2936
|
/**
|
|
2823
|
-
*
|
|
2824
|
-
*
|
|
2937
|
+
* The state of the schema, taking no table lock. Parameters: `[marker]`. Columns: `marked`, whether
|
|
2938
|
+
* the lane table carries `marker` as its comment (the schema statements that produce it already
|
|
2939
|
+
* ran), and `payload_json`, whether the payload and headers columns of the event table are both
|
|
2940
|
+
* `json` (the conversion of the `jsonb` columns of schemas before 0.4.1 ran).
|
|
2825
2941
|
*/
|
|
2826
2942
|
get schemaCurrent() {
|
|
2827
2943
|
return `SELECT EXISTS (
|
|
@@ -2830,10 +2946,21 @@ var SetupStatements = class {
|
|
|
2830
2946
|
JOIN pg_catalog.pg_description AS d
|
|
2831
2947
|
ON d.objoid = c.oid AND d.classoid = 'pg_catalog.pg_class'::regclass AND d.objsubid = 0
|
|
2832
2948
|
WHERE n.nspname = '${this.#names.schema}' AND c.relname = '${this.#names.lanesTableName}' AND d.description = $1::text
|
|
2833
|
-
) AS
|
|
2949
|
+
) AS marked, (
|
|
2950
|
+
SELECT count(*) FROM pg_catalog.pg_attribute AS a
|
|
2951
|
+
JOIN pg_catalog.pg_class AS c ON c.oid = a.attrelid
|
|
2952
|
+
JOIN pg_catalog.pg_namespace AS n ON n.oid = c.relnamespace
|
|
2953
|
+
WHERE n.nspname = '${this.#names.schema}' AND c.relname = '${this.#names.eventsTableName}'
|
|
2954
|
+
AND a.attname IN ('payload', 'headers') AND a.atttypid = 'pg_catalog.json'::pg_catalog.regtype
|
|
2955
|
+
AND NOT a.attisdropped
|
|
2956
|
+
) = 2 AS payload_json`;
|
|
2834
2957
|
}
|
|
2958
|
+
/**
|
|
2959
|
+
* Takes the setup lock. Returns a boolean column rather than the `void` of the lock function, which
|
|
2960
|
+
* some libraries cannot read (Prisma's raw queries reject a `void` column).
|
|
2961
|
+
*/
|
|
2835
2962
|
get setupLock() {
|
|
2836
|
-
return "SELECT pg_advisory_xact_lock(hashtextextended($1::text, 0))";
|
|
2963
|
+
return "SELECT true AS locked FROM pg_advisory_xact_lock(hashtextextended($1::text, 0))";
|
|
2837
2964
|
}
|
|
2838
2965
|
};
|
|
2839
2966
|
|
|
@@ -2864,6 +2991,7 @@ var PostgresEventStore = class {
|
|
|
2864
2991
|
#claims;
|
|
2865
2992
|
#completion;
|
|
2866
2993
|
#maintenance;
|
|
2994
|
+
#setup;
|
|
2867
2995
|
#owned = new OwnedConnections();
|
|
2868
2996
|
constructor(options) {
|
|
2869
2997
|
const { purgeBatchSize, wakeUp, queryOptions } = resolveStoreSettings(options);
|
|
@@ -2877,7 +3005,7 @@ var PostgresEventStore = class {
|
|
|
2877
3005
|
statements,
|
|
2878
3006
|
schemaStatements,
|
|
2879
3007
|
setupLockKey: statements.setup.setupLockKey,
|
|
2880
|
-
schemaMarker:
|
|
3008
|
+
schemaMarker: version040SchemaMarker(options),
|
|
2881
3009
|
purgeBatchSize,
|
|
2882
3010
|
completionHistory: names.completionHistory,
|
|
2883
3011
|
logger,
|
|
@@ -2901,42 +3029,20 @@ var PostgresEventStore = class {
|
|
|
2901
3029
|
this.#claims = new PostgresClaims(state);
|
|
2902
3030
|
this.#completion = new PostgresCompletion(state);
|
|
2903
3031
|
this.#maintenance = new PostgresMaintenance(state);
|
|
3032
|
+
this.#setup = new PostgresSetup(state, options);
|
|
2904
3033
|
}
|
|
2905
3034
|
/**
|
|
2906
3035
|
* Creates the schema, tables and indexes if they do not exist. Safe to call
|
|
2907
3036
|
* from many processes at once: a transaction-scoped advisory lock serializes
|
|
2908
3037
|
* the DDL, so racing `CREATE TABLE IF NOT EXISTS` statements cannot collide.
|
|
2909
|
-
* When the lane table already carries the marker
|
|
2910
|
-
*
|
|
3038
|
+
* When the lane table already carries the schema marker ({@link version040SchemaMarker})
|
|
3039
|
+
* and the payload columns are `json`, nothing runs and no table is locked, so instances
|
|
2911
3040
|
* that start next to running dispatchers cannot deadlock with their claims.
|
|
2912
3041
|
* Skip it (`setupStore: false`) when a migration tool applies
|
|
2913
3042
|
* `createPostgresSchemaSql()` instead.
|
|
2914
3043
|
*/
|
|
2915
3044
|
async setup() {
|
|
2916
|
-
|
|
2917
|
-
return;
|
|
2918
|
-
}
|
|
2919
|
-
await this.#state.executor.transaction(async (transaction) => {
|
|
2920
|
-
await this.#state.executor.query(
|
|
2921
|
-
this.#state.statements.setup.setupLock,
|
|
2922
|
-
[this.#state.setupLockKey],
|
|
2923
|
-
transaction
|
|
2924
|
-
);
|
|
2925
|
-
if (await this.#schemaIsCurrent(transaction)) {
|
|
2926
|
-
return;
|
|
2927
|
-
}
|
|
2928
|
-
for (const statement of this.#state.schemaStatements) {
|
|
2929
|
-
await this.#state.executor.query(statement, [], transaction);
|
|
2930
|
-
}
|
|
2931
|
-
});
|
|
2932
|
-
}
|
|
2933
|
-
async #schemaIsCurrent(transaction) {
|
|
2934
|
-
const rows = await this.#state.executor.query(
|
|
2935
|
-
this.#state.statements.setup.schemaCurrent,
|
|
2936
|
-
[this.#state.schemaMarker],
|
|
2937
|
-
transaction
|
|
2938
|
-
);
|
|
2939
|
-
return rows[0]["current"] === true;
|
|
3045
|
+
await this.#setup.run();
|
|
2940
3046
|
}
|
|
2941
3047
|
/**
|
|
2942
3048
|
* Runs `work` in a transaction of the executor. With `options.timeoutMs` (from `bus.transaction`)
|
|
@@ -3102,7 +3208,12 @@ var PostgresEventStore = class {
|
|
|
3102
3208
|
};
|
|
3103
3209
|
function connect(options, { logger, owned }) {
|
|
3104
3210
|
if (options.executor !== void 0) {
|
|
3105
|
-
|
|
3211
|
+
const { executor: executor2, pool: pool2 } = options;
|
|
3212
|
+
return {
|
|
3213
|
+
executor: pool2 === void 0 ? executor2 : withRootPool(executor2, new PgExecutor(pool2, { logger })),
|
|
3214
|
+
pool: pool2,
|
|
3215
|
+
ownedPool: void 0
|
|
3216
|
+
};
|
|
3106
3217
|
}
|
|
3107
3218
|
const pool = options.pool ?? new Pool({
|
|
3108
3219
|
...options.poolConfig,
|