@relaybus/postgres 0.3.0 → 0.3.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/dist/chunk-NNZJR5AB.js +12 -0
- package/dist/chunk-NNZJR5AB.js.map +1 -0
- package/dist/drizzle.cjs +13 -6
- package/dist/drizzle.cjs.map +1 -1
- package/dist/drizzle.js +6 -5
- package/dist/drizzle.js.map +1 -1
- package/dist/index.cjs +428 -401
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +429 -402
- package/dist/index.js.map +1 -1
- package/dist/knex.cjs +14 -5
- package/dist/knex.cjs.map +1 -1
- package/dist/knex.js +6 -5
- package/dist/knex.js.map +1 -1
- package/dist/kysely.cjs +13 -6
- package/dist/kysely.cjs.map +1 -1
- package/dist/kysely.js +6 -6
- package/dist/kysely.js.map +1 -1
- package/dist/prisma.cjs +14 -5
- package/dist/prisma.cjs.map +1 -1
- package/dist/prisma.js +7 -5
- package/dist/prisma.js.map +1 -1
- package/dist/typeorm.cjs +14 -5
- package/dist/typeorm.cjs.map +1 -1
- package/dist/typeorm.js +7 -5
- package/dist/typeorm.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
import { ConfigurationError, RelayBusError, systemClock } from '@relaybus/core';
|
|
3
|
-
import {
|
|
3
|
+
import { LaneLeaseTokenFormat, HandedOutLedger, PositiveInteger, schedule, OwnerSerializedClaims, LeaseSettlement, CompositeKey, DeliveryLeaseIdentity } from '@relaybus/core/internal';
|
|
4
4
|
import { Client, Pool } from 'pg';
|
|
5
5
|
|
|
6
6
|
// src/pg-executor.ts
|
|
@@ -187,6 +187,26 @@ var DEFAULT_SCHEMA = "public";
|
|
|
187
187
|
var MAXIMUM_CHANNEL_LENGTH = 63;
|
|
188
188
|
var DEFAULT_TABLE_PREFIX = "relaybus_";
|
|
189
189
|
var TABLE_PREFIX_PATTERN = /^[a-z0-9_]*$/;
|
|
190
|
+
function indexNames(index) {
|
|
191
|
+
return {
|
|
192
|
+
deliveryEventHandler: index("deliveries_event_handler_unique"),
|
|
193
|
+
deliveryLaneDue: index("deliveries_lane_due_idx"),
|
|
194
|
+
deliveryKeyPending: index("deliveries_key_pending_idx"),
|
|
195
|
+
eventCreated: index("events_created_idx"),
|
|
196
|
+
deliveryCompleted: index("deliveries_completed_idx"),
|
|
197
|
+
deliveryDead: index("deliveries_dead_idx"),
|
|
198
|
+
completionCompleted: index("completed_deliveries_completed_idx"),
|
|
199
|
+
eventCompletionsCompleted: index("event_completions_completed_idx"),
|
|
200
|
+
obsolete: [
|
|
201
|
+
index("deliveries_due_idx"),
|
|
202
|
+
index("deliveries_due_unkeyed_idx"),
|
|
203
|
+
index("deliveries_handler_created_idx"),
|
|
204
|
+
index("deliveries_key_unfinished_idx"),
|
|
205
|
+
index("deliveries_key_processing_idx"),
|
|
206
|
+
index("deliveries_status_created_idx")
|
|
207
|
+
]
|
|
208
|
+
};
|
|
209
|
+
}
|
|
190
210
|
function resolvePostgresNames(options = {}) {
|
|
191
211
|
const schema = options.schema ?? DEFAULT_SCHEMA;
|
|
192
212
|
const tablePrefix = options.tablePrefix ?? DEFAULT_TABLE_PREFIX;
|
|
@@ -216,24 +236,7 @@ function resolvePostgresNames(options = {}) {
|
|
|
216
236
|
completions: table("completed_deliveries"),
|
|
217
237
|
completionHistory: options.completionHistory ?? false,
|
|
218
238
|
notificationChannel: channelName(`${schema}.${tablePrefix}deliveries`),
|
|
219
|
-
indexes:
|
|
220
|
-
deliveryEventHandler: index("deliveries_event_handler_unique"),
|
|
221
|
-
deliveryLaneDue: index("deliveries_lane_due_idx"),
|
|
222
|
-
deliveryKeyPending: index("deliveries_key_pending_idx"),
|
|
223
|
-
eventCreated: index("events_created_idx"),
|
|
224
|
-
deliveryCompleted: index("deliveries_completed_idx"),
|
|
225
|
-
deliveryDead: index("deliveries_dead_idx"),
|
|
226
|
-
completionCompleted: index("completed_deliveries_completed_idx"),
|
|
227
|
-
eventCompletionsCompleted: index("event_completions_completed_idx"),
|
|
228
|
-
obsolete: [
|
|
229
|
-
index("deliveries_due_idx"),
|
|
230
|
-
index("deliveries_due_unkeyed_idx"),
|
|
231
|
-
index("deliveries_handler_created_idx"),
|
|
232
|
-
index("deliveries_key_unfinished_idx"),
|
|
233
|
-
index("deliveries_key_processing_idx"),
|
|
234
|
-
index("deliveries_status_created_idx")
|
|
235
|
-
]
|
|
236
|
-
}
|
|
239
|
+
indexes: indexNames(index)
|
|
237
240
|
};
|
|
238
241
|
}
|
|
239
242
|
var CHANNEL_HASH_DIGITS = 32;
|
|
@@ -264,6 +267,9 @@ function createPostgresSchemaStatements(options = {}) {
|
|
|
264
267
|
return statements;
|
|
265
268
|
}
|
|
266
269
|
function eventAndDeliveryStatements(names) {
|
|
270
|
+
return [...eventAndKeySequenceStatements(names), ...deliveryStatements(names)];
|
|
271
|
+
}
|
|
272
|
+
function eventAndKeySequenceStatements(names) {
|
|
267
273
|
return [
|
|
268
274
|
`CREATE TABLE IF NOT EXISTS ${names.events} (
|
|
269
275
|
id text PRIMARY KEY,
|
|
@@ -283,7 +289,11 @@ function eventAndDeliveryStatements(names) {
|
|
|
283
289
|
`CREATE TABLE IF NOT EXISTS ${names.keySequences} (
|
|
284
290
|
key text PRIMARY KEY,
|
|
285
291
|
sequence bigint NOT NULL
|
|
286
|
-
)
|
|
292
|
+
)`
|
|
293
|
+
];
|
|
294
|
+
}
|
|
295
|
+
function deliveryStatements(names) {
|
|
296
|
+
return [
|
|
287
297
|
`CREATE TABLE IF NOT EXISTS ${names.deliveries} (
|
|
288
298
|
id text COLLATE "C" PRIMARY KEY,
|
|
289
299
|
event_id text NOT NULL REFERENCES ${names.events} (id) ON DELETE CASCADE,
|
|
@@ -375,6 +385,16 @@ function createPostgresSchemaSql(options = {}) {
|
|
|
375
385
|
`;
|
|
376
386
|
}
|
|
377
387
|
var RECONNECT_DELAY_MS = 1e3;
|
|
388
|
+
function endOnce(connection, ended) {
|
|
389
|
+
let ending = Promise.resolve();
|
|
390
|
+
return () => {
|
|
391
|
+
if (!ended.value) {
|
|
392
|
+
ended.value = true;
|
|
393
|
+
ending = connection.end().catch(() => void 0);
|
|
394
|
+
}
|
|
395
|
+
return ending;
|
|
396
|
+
};
|
|
397
|
+
}
|
|
378
398
|
var NotificationListener = class {
|
|
379
399
|
#connect;
|
|
380
400
|
#channel;
|
|
@@ -393,15 +413,8 @@ var NotificationListener = class {
|
|
|
393
413
|
/** Connects and listens. Rejects when that fails; a connection lost later is replaced. */
|
|
394
414
|
async start() {
|
|
395
415
|
const connection = await this.#connect();
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
const end = () => {
|
|
399
|
-
if (!ended) {
|
|
400
|
-
ended = true;
|
|
401
|
-
ending = connection.end().catch(() => void 0);
|
|
402
|
-
}
|
|
403
|
-
return ending;
|
|
404
|
-
};
|
|
416
|
+
const ended = { value: false };
|
|
417
|
+
const end = endOnce(connection, ended);
|
|
405
418
|
if (this.#closed) {
|
|
406
419
|
await end();
|
|
407
420
|
return;
|
|
@@ -409,7 +422,7 @@ var NotificationListener = class {
|
|
|
409
422
|
this.#drop = end;
|
|
410
423
|
let listening = false;
|
|
411
424
|
const onLost = (error) => {
|
|
412
|
-
if (!listening || ended) {
|
|
425
|
+
if (!listening || ended.value) {
|
|
413
426
|
return;
|
|
414
427
|
}
|
|
415
428
|
void end();
|
|
@@ -425,14 +438,8 @@ var NotificationListener = class {
|
|
|
425
438
|
try {
|
|
426
439
|
await connection.query(`LISTEN "${this.#channel}"`);
|
|
427
440
|
} catch (error) {
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
await end();
|
|
432
|
-
if (this.#closed) {
|
|
433
|
-
return;
|
|
434
|
-
}
|
|
435
|
-
throw error;
|
|
441
|
+
await this.#abandonFailedListen(end, error);
|
|
442
|
+
return;
|
|
436
443
|
}
|
|
437
444
|
if (this.#closed) {
|
|
438
445
|
await end();
|
|
@@ -440,6 +447,17 @@ var NotificationListener = class {
|
|
|
440
447
|
}
|
|
441
448
|
listening = true;
|
|
442
449
|
}
|
|
450
|
+
/** Forgets and ends a connection whose LISTEN failed; rethrows the failure unless closing caused it. */
|
|
451
|
+
async #abandonFailedListen(end, error) {
|
|
452
|
+
if (this.#drop === end) {
|
|
453
|
+
this.#drop = void 0;
|
|
454
|
+
}
|
|
455
|
+
await end();
|
|
456
|
+
if (this.#closed) {
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
throw error;
|
|
460
|
+
}
|
|
443
461
|
#scheduleReconnect() {
|
|
444
462
|
this.#reconnectTimer = schedule(systemClock, {
|
|
445
463
|
run: () => {
|
|
@@ -473,8 +491,6 @@ var NotificationListener = class {
|
|
|
473
491
|
await drop?.();
|
|
474
492
|
}
|
|
475
493
|
};
|
|
476
|
-
|
|
477
|
-
// src/row-mapping.ts
|
|
478
494
|
function text(row, column) {
|
|
479
495
|
return row[column];
|
|
480
496
|
}
|
|
@@ -501,16 +517,16 @@ function mapEventEnvelope(row) {
|
|
|
501
517
|
occurredAt: new Date(text(row, "event_occurred_at"))
|
|
502
518
|
};
|
|
503
519
|
}
|
|
520
|
+
var LEASE_TOKENS = new LaneLeaseTokenFormat({
|
|
521
|
+
laneCount: LANE_COUNT,
|
|
522
|
+
epochDigits: 18,
|
|
523
|
+
claimsDigits: 9
|
|
524
|
+
});
|
|
504
525
|
function formatLeaseToken(lane, { epoch, claims }) {
|
|
505
|
-
return
|
|
526
|
+
return LEASE_TOKENS.format(lane, { epoch, claims });
|
|
506
527
|
}
|
|
507
|
-
var LEASE_TOKEN_PATTERN = /^(\d{1,3}):(\d{1,18}):(\d{1,9})$/;
|
|
508
528
|
function parseLeaseToken(token) {
|
|
509
|
-
|
|
510
|
-
if (match === null || Number(match[1]) >= LANE_COUNT) {
|
|
511
|
-
return void 0;
|
|
512
|
-
}
|
|
513
|
-
return { lane: Number(match[1]), epoch: match[2], claims: Number(match[3]) };
|
|
529
|
+
return LEASE_TOKENS.parse(token);
|
|
514
530
|
}
|
|
515
531
|
function mapClaimedDelivery(row, leaseExpiresAt) {
|
|
516
532
|
return {
|
|
@@ -562,22 +578,11 @@ var PostgresAppend = class {
|
|
|
562
578
|
const createdAt = now.toISOString();
|
|
563
579
|
const wakeAt = this.#wakeAt(plan.deliveries, { now, transaction });
|
|
564
580
|
const handlersByEvent = handlersByEventOf(plan.deliveries);
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
plan.events.map((event, ordinal) => ({
|
|
571
|
-
ordinal,
|
|
572
|
-
...toEventRow(event, { sequence: null, handlers: handlersByEvent.get(event.id) ?? [] })
|
|
573
|
-
}))
|
|
574
|
-
),
|
|
575
|
-
createdAt,
|
|
576
|
-
JSON.stringify(plan.deliveries),
|
|
577
|
-
JSON.stringify([...keyCounts].map(([key, count]) => ({ key, count }))),
|
|
578
|
-
wakeAt
|
|
579
|
-
],
|
|
580
|
-
transaction
|
|
581
|
+
const fresh = await this.#insertFreshEvents(transaction, {
|
|
582
|
+
plan,
|
|
583
|
+
handlersByEvent,
|
|
584
|
+
createdAt,
|
|
585
|
+
wakeAt
|
|
581
586
|
});
|
|
582
587
|
const insertedEventIds = new Set(
|
|
583
588
|
fresh.filter((row) => row["inserted"] === true).map((row) => row["id"])
|
|
@@ -599,6 +604,40 @@ var PostgresAppend = class {
|
|
|
599
604
|
}
|
|
600
605
|
return { ...splitOutcomes(plan, insertedEventIds), appendedDeliveryCount };
|
|
601
606
|
}
|
|
607
|
+
/** Runs the publishing statement that inserts the events not stored yet, with their deliveries. */
|
|
608
|
+
async #insertFreshEvents(transaction, {
|
|
609
|
+
plan,
|
|
610
|
+
handlersByEvent,
|
|
611
|
+
createdAt,
|
|
612
|
+
wakeAt
|
|
613
|
+
}) {
|
|
614
|
+
const keyCounts = keyCountsOf(plan.events);
|
|
615
|
+
return this.#state.query({
|
|
616
|
+
sql: this.#state.statements.append.insertFreshEvents,
|
|
617
|
+
parameters: [
|
|
618
|
+
JSON.stringify(
|
|
619
|
+
plan.events.map((event, ordinal) => ({
|
|
620
|
+
ordinal,
|
|
621
|
+
...toEventRow(event, { sequence: null, handlers: handlersByEvent.get(event.id) ?? [] })
|
|
622
|
+
}))
|
|
623
|
+
),
|
|
624
|
+
createdAt,
|
|
625
|
+
JSON.stringify(plan.deliveries),
|
|
626
|
+
JSON.stringify([...keyCounts].map(([key, count]) => ({ key, count }))),
|
|
627
|
+
wakeAt
|
|
628
|
+
],
|
|
629
|
+
transaction
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
/** Locks the given events and returns the ids of those that still exist. */
|
|
633
|
+
async #lockExistingEvents(transaction, existing) {
|
|
634
|
+
const locked = await this.#state.query({
|
|
635
|
+
sql: this.#state.statements.append.lockExistingEvents,
|
|
636
|
+
parameters: [JSON.stringify(existing.map((row) => row.id).sort())],
|
|
637
|
+
transaction
|
|
638
|
+
});
|
|
639
|
+
return new Set(locked.map((row) => row["id"]));
|
|
640
|
+
}
|
|
602
641
|
/**
|
|
603
642
|
* The time the publishing statements notify the dispatchers for (deliveries due by then wake them),
|
|
604
643
|
* or `null` for no notification. An append in a transaction of the caller's always notifies, at its
|
|
@@ -666,13 +705,7 @@ var PostgresAppend = class {
|
|
|
666
705
|
})
|
|
667
706
|
);
|
|
668
707
|
while (existing.length > 0) {
|
|
669
|
-
const locked =
|
|
670
|
-
(await this.#state.query({
|
|
671
|
-
sql: this.#state.statements.append.lockExistingEvents,
|
|
672
|
-
parameters: [JSON.stringify(existing.map((row) => row.id).sort())],
|
|
673
|
-
transaction
|
|
674
|
-
})).map((row) => row["id"])
|
|
675
|
-
);
|
|
708
|
+
const locked = await this.#lockExistingEvents(transaction, existing);
|
|
676
709
|
const gone = existing.filter((row) => !locked.has(row.id));
|
|
677
710
|
if (gone.length > 0) {
|
|
678
711
|
const inserted2 = await this.#state.query({
|
|
@@ -709,7 +742,7 @@ function planAppend(entries) {
|
|
|
709
742
|
events.push(event);
|
|
710
743
|
}
|
|
711
744
|
for (const delivery of newDeliveries) {
|
|
712
|
-
const identity =
|
|
745
|
+
const identity = CompositeKey.of(event.id, delivery.handler);
|
|
713
746
|
if (!seenDeliveries.has(identity)) {
|
|
714
747
|
seenDeliveries.add(identity);
|
|
715
748
|
deliveries.push({
|
|
@@ -774,49 +807,46 @@ var ReconcileFailedError = class extends RelayBusError {
|
|
|
774
807
|
name = "ReconcileFailedError";
|
|
775
808
|
code = "RELAYBUS_POSTGRES_RECONCILE_FAILED";
|
|
776
809
|
};
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
810
|
+
function leftWorkBehind(row) {
|
|
811
|
+
return row["takeover_waiting"] === true || row["unexamined_free_lanes"] === true || row["stale"] === true;
|
|
812
|
+
}
|
|
813
|
+
function keepKeyHeads(picked, limit) {
|
|
814
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
815
|
+
const kept = [...picked].sort((left, right) => compareSequence(left, right)).filter((row) => {
|
|
816
|
+
if (row["delivery_key"] === null) {
|
|
817
|
+
return true;
|
|
818
|
+
}
|
|
819
|
+
const group = CompositeKey.of(row["handler"], row["delivery_key"]);
|
|
820
|
+
return !seenKeys.has(group) && Boolean(seenKeys.add(group));
|
|
821
|
+
});
|
|
822
|
+
kept.sort(compareDue);
|
|
823
|
+
return kept.slice(0, limit);
|
|
824
|
+
}
|
|
825
|
+
function compareSequence(left, right) {
|
|
826
|
+
const first = sequenceOf(left);
|
|
827
|
+
const second = sequenceOf(right);
|
|
828
|
+
return Number(first > second) - Number(first < second);
|
|
829
|
+
}
|
|
830
|
+
function sequenceOf(row) {
|
|
831
|
+
return BigInt(row["sequence"] ?? 0);
|
|
832
|
+
}
|
|
833
|
+
function compareDue(left, right) {
|
|
834
|
+
return compareText(left["due_at"], right["due_at"]) || compareText(left["delivery_id"], right["delivery_id"]);
|
|
835
|
+
}
|
|
836
|
+
function compareText(left, right) {
|
|
837
|
+
return Number(left > right) - Number(left < right);
|
|
781
838
|
}
|
|
782
839
|
|
|
783
840
|
// src/postgres-claim.ts
|
|
784
841
|
var INITIAL_LANES_PER_ROUND = 4;
|
|
785
842
|
var MAXIMUM_STALE_CLAIM_ATTEMPTS = 3;
|
|
786
|
-
var PostgresClaims = class {
|
|
843
|
+
var PostgresClaims = class extends OwnerSerializedClaims {
|
|
787
844
|
#state;
|
|
788
845
|
constructor(state) {
|
|
846
|
+
super();
|
|
789
847
|
this.#state = state;
|
|
790
848
|
}
|
|
791
|
-
|
|
792
|
-
claim(request) {
|
|
793
|
-
if (request.handlers.length === 0 || !(request.limit > 0)) {
|
|
794
|
-
return Promise.resolve([]);
|
|
795
|
-
}
|
|
796
|
-
return this.#serialized(request.leaseOwner, () => this.#claim(request));
|
|
797
|
-
}
|
|
798
|
-
/**
|
|
799
|
-
* Runs `work` after the earlier claims of the same owner on this store settled. The
|
|
800
|
-
* bookkeeping of what was handed out and the reconciliation of a failed claim need
|
|
801
|
-
* one claim at a time per owner (the dispatcher claims one batch at a time anyway;
|
|
802
|
-
* `runOnce` next to a running dispatcher is what this guards against).
|
|
803
|
-
*/
|
|
804
|
-
#serialized(owner, work) {
|
|
805
|
-
const previous = this.#state.ownerQueues.get(owner) ?? Promise.resolve();
|
|
806
|
-
const run = previous.then(work, work);
|
|
807
|
-
const tail = run.then(
|
|
808
|
-
() => void 0,
|
|
809
|
-
() => void 0
|
|
810
|
-
);
|
|
811
|
-
this.#state.ownerQueues.set(owner, tail);
|
|
812
|
-
void tail.then(() => {
|
|
813
|
-
if (this.#state.ownerQueues.get(owner) === tail) {
|
|
814
|
-
this.#state.ownerQueues.delete(owner);
|
|
815
|
-
}
|
|
816
|
-
});
|
|
817
|
-
return run;
|
|
818
|
-
}
|
|
819
|
-
async #claim(request) {
|
|
849
|
+
async claimInTurn(request) {
|
|
820
850
|
await this.#assertReconciled(request.leaseOwner);
|
|
821
851
|
const heldFirst = this.#state.drainingOwners.has(request.leaseOwner);
|
|
822
852
|
let rows = await this.#claimFirst(request, heldFirst);
|
|
@@ -988,7 +1018,7 @@ var PostgresClaims = class {
|
|
|
988
1018
|
#handOut(rows, leaseOwner) {
|
|
989
1019
|
for (const row of rows) {
|
|
990
1020
|
const claim = mapClaimedDelivery(row, /* @__PURE__ */ new Date(0));
|
|
991
|
-
this.#state.handedOut.
|
|
1021
|
+
this.#state.handedOut.register(claim, {
|
|
992
1022
|
owner: leaseOwner,
|
|
993
1023
|
deliveryId: claim.deliveryId,
|
|
994
1024
|
...parseLeaseToken(claim.leaseToken)
|
|
@@ -1009,11 +1039,9 @@ var PostgresClaims = class {
|
|
|
1009
1039
|
...JSON.parse(row["running_ids"]),
|
|
1010
1040
|
...claimed.map((picked) => picked["delivery_id"])
|
|
1011
1041
|
]);
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1042
|
+
this.#state.handedOut.forgetWhere(
|
|
1043
|
+
(entry) => entry.owner === leaseOwner && heldLanes.has(entry.lane) && !running.has(entry.deliveryId)
|
|
1044
|
+
);
|
|
1017
1045
|
}
|
|
1018
1046
|
/**
|
|
1019
1047
|
* After a claim failed, possibly after the database recorded its batch: gives back the recorded
|
|
@@ -1023,7 +1051,7 @@ var PostgresClaims = class {
|
|
|
1023
1051
|
*/
|
|
1024
1052
|
async #reconcile(leaseOwner) {
|
|
1025
1053
|
const generation = this.#generation(leaseOwner, true);
|
|
1026
|
-
const handedOut =
|
|
1054
|
+
const handedOut = this.#state.handedOut.entriesOf(leaseOwner).map(({ deliveryId, lane, epoch, claims }) => ({ id: deliveryId, lane, epoch, claims }));
|
|
1027
1055
|
try {
|
|
1028
1056
|
await this.#state.query({
|
|
1029
1057
|
sql: this.#state.statements.claim.reconcileClaims,
|
|
@@ -1060,16 +1088,7 @@ var PostgresClaims = class {
|
|
|
1060
1088
|
(row) => row["delivery_id"] !== null && !pickedIds.has(row["delivery_id"])
|
|
1061
1089
|
)
|
|
1062
1090
|
);
|
|
1063
|
-
|
|
1064
|
-
const kept = [...picked].sort((left, right) => compareSequence(left, right)).filter((row) => {
|
|
1065
|
-
if (row["delivery_key"] === null) {
|
|
1066
|
-
return true;
|
|
1067
|
-
}
|
|
1068
|
-
const group = `${row["handler"]}\0${row["delivery_key"]}`;
|
|
1069
|
-
return !seenKeys.has(group) && Boolean(seenKeys.add(group));
|
|
1070
|
-
});
|
|
1071
|
-
kept.sort(compareDue);
|
|
1072
|
-
return kept.slice(0, limit);
|
|
1091
|
+
return keepKeyHeads(picked, limit);
|
|
1073
1092
|
}
|
|
1074
1093
|
return picked;
|
|
1075
1094
|
}
|
|
@@ -1101,43 +1120,12 @@ var PostgresClaims = class {
|
|
|
1101
1120
|
}
|
|
1102
1121
|
};
|
|
1103
1122
|
var GENERATION_STEP = 1n;
|
|
1104
|
-
|
|
1105
|
-
return row["takeover_waiting"] === true || row["unexamined_free_lanes"] === true || row["stale"] === true;
|
|
1106
|
-
}
|
|
1107
|
-
function compareSequence(left, right) {
|
|
1108
|
-
const first = sequenceOf(left);
|
|
1109
|
-
const second = sequenceOf(right);
|
|
1110
|
-
return Number(first > second) - Number(first < second);
|
|
1111
|
-
}
|
|
1112
|
-
function sequenceOf(row) {
|
|
1113
|
-
return BigInt(row["sequence"] ?? 0);
|
|
1114
|
-
}
|
|
1115
|
-
function compareDue(left, right) {
|
|
1116
|
-
return compareText(left["due_at"], right["due_at"]) || compareText(left["delivery_id"], right["delivery_id"]);
|
|
1117
|
-
}
|
|
1118
|
-
function compareText(left, right) {
|
|
1119
|
-
return Number(left > right) - Number(left < right);
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
// src/postgres-completion.ts
|
|
1123
|
-
var PostgresCompletion = class {
|
|
1123
|
+
var PostgresCompletion = class extends LeaseSettlement {
|
|
1124
1124
|
#state;
|
|
1125
1125
|
constructor(state) {
|
|
1126
|
+
super();
|
|
1126
1127
|
this.#state = state;
|
|
1127
1128
|
}
|
|
1128
|
-
/** Implements {@link PostgresEventStore.extendLease}. */
|
|
1129
|
-
extendLease(lease, leaseExpiresAt) {
|
|
1130
|
-
return this.#whenIssued(
|
|
1131
|
-
lease,
|
|
1132
|
-
(held) => this.#changed(this.#state.statements.completion.extendLease, [
|
|
1133
|
-
lease.deliveryId,
|
|
1134
|
-
held.lane,
|
|
1135
|
-
held.epoch,
|
|
1136
|
-
held.claims,
|
|
1137
|
-
leaseExpiresAt.toISOString()
|
|
1138
|
-
])
|
|
1139
|
-
);
|
|
1140
|
-
}
|
|
1141
1129
|
/** Implements {@link PostgresEventStore.complete}. */
|
|
1142
1130
|
async complete(lease, { now, transaction }) {
|
|
1143
1131
|
return (await this.#completeLeases([lease], { now, transaction }))[0];
|
|
@@ -1146,76 +1134,44 @@ var PostgresCompletion = class {
|
|
|
1146
1134
|
completeMany(leases, now) {
|
|
1147
1135
|
return this.#completeLeases(leases, { now, transaction: void 0 });
|
|
1148
1136
|
}
|
|
1149
|
-
/**
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
error,
|
|
1153
|
-
now
|
|
1154
|
-
}) {
|
|
1155
|
-
return this.#settled(
|
|
1156
|
-
lease,
|
|
1157
|
-
this.#whenIssued(
|
|
1158
|
-
lease,
|
|
1159
|
-
(held) => this.#changed(this.#state.statements.completion.reschedule, [
|
|
1160
|
-
lease.deliveryId,
|
|
1161
|
-
held.lane,
|
|
1162
|
-
held.epoch,
|
|
1163
|
-
held.claims,
|
|
1164
|
-
availableAt.toISOString(),
|
|
1165
|
-
JSON.stringify(error),
|
|
1166
|
-
now.toISOString()
|
|
1167
|
-
])
|
|
1168
|
-
)
|
|
1169
|
-
);
|
|
1137
|
+
/** A lease this store issued; any other token cannot be held. */
|
|
1138
|
+
findHeld(lease) {
|
|
1139
|
+
return parseLeaseToken(lease.leaseToken);
|
|
1170
1140
|
}
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
lease,
|
|
1177
|
-
(held) => this.#changed(this.#state.statements.completion.bury, [
|
|
1178
|
-
lease.deliveryId,
|
|
1179
|
-
held.lane,
|
|
1180
|
-
held.epoch,
|
|
1181
|
-
held.claims,
|
|
1182
|
-
JSON.stringify(error),
|
|
1183
|
-
now.toISOString()
|
|
1184
|
-
])
|
|
1185
|
-
)
|
|
1186
|
-
);
|
|
1141
|
+
writeExtension(held, { lease, leaseExpiresAt }) {
|
|
1142
|
+
return this.#changed(this.#state.statements.completion.extendLease, [
|
|
1143
|
+
...fenceOf(lease, held),
|
|
1144
|
+
leaseExpiresAt.toISOString()
|
|
1145
|
+
]);
|
|
1187
1146
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
this.#
|
|
1193
|
-
|
|
1194
|
-
(
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1147
|
+
writeChange(held, { lease, change }) {
|
|
1148
|
+
const { completion } = this.#state.statements;
|
|
1149
|
+
const fence = fenceOf(lease, held);
|
|
1150
|
+
if (change.kind === "reschedule") {
|
|
1151
|
+
return this.#changed(completion.reschedule, [
|
|
1152
|
+
...fence,
|
|
1153
|
+
change.availableAt.toISOString(),
|
|
1154
|
+
JSON.stringify(change.error),
|
|
1155
|
+
change.now.toISOString()
|
|
1156
|
+
]);
|
|
1157
|
+
}
|
|
1158
|
+
if (change.kind === "bury") {
|
|
1159
|
+
return this.#changed(completion.bury, [
|
|
1160
|
+
...fence,
|
|
1161
|
+
JSON.stringify(change.error),
|
|
1162
|
+
change.now.toISOString()
|
|
1163
|
+
]);
|
|
1164
|
+
}
|
|
1165
|
+
return this.#changed(completion.release, [...fence, change.now.toISOString()]);
|
|
1203
1166
|
}
|
|
1204
1167
|
/** Forgets a handed-out lease once the store answered about it (any answer: the claim is over or fenced out). */
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
this.#state.handedOut.delete(handedOutKey(lease.deliveryId, lease.leaseToken));
|
|
1208
|
-
return result;
|
|
1168
|
+
forgetHandedOut(lease) {
|
|
1169
|
+
this.#state.handedOut.forget(lease);
|
|
1209
1170
|
}
|
|
1210
1171
|
async #changed(sql, parameters) {
|
|
1211
1172
|
const rows = await this.#state.query({ sql, parameters });
|
|
1212
1173
|
return readCount(rows[0], "count") > 0;
|
|
1213
1174
|
}
|
|
1214
|
-
/** Runs `operation` for a lease this store issued; any other token cannot be held. */
|
|
1215
|
-
async #whenIssued(lease, operation) {
|
|
1216
|
-
const held = parseLeaseToken(lease.leaseToken);
|
|
1217
|
-
return held !== void 0 && await operation(held);
|
|
1218
|
-
}
|
|
1219
1175
|
/**
|
|
1220
1176
|
* Deletes the delivery of every lease that is still the current claim of its
|
|
1221
1177
|
* delivery. Returns one flag per lease, in order. Results are matched to leases
|
|
@@ -1223,16 +1179,7 @@ var PostgresCompletion = class {
|
|
|
1223
1179
|
* their own answers; a lease listed twice completes once, at its first position.
|
|
1224
1180
|
*/
|
|
1225
1181
|
async #completeLeases(leases, { now, transaction }) {
|
|
1226
|
-
const issued =
|
|
1227
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1228
|
-
leases.forEach((lease, ord) => {
|
|
1229
|
-
const held = parseLeaseToken(lease.leaseToken);
|
|
1230
|
-
const identity = `${lease.deliveryId}\0${lease.leaseToken}`;
|
|
1231
|
-
if (held !== void 0 && !seen.has(identity)) {
|
|
1232
|
-
seen.add(identity);
|
|
1233
|
-
issued.push({ ord, id: lease.deliveryId, ...held });
|
|
1234
|
-
}
|
|
1235
|
-
});
|
|
1182
|
+
const issued = issuedLeases(leases);
|
|
1236
1183
|
const completed = /* @__PURE__ */ new Set();
|
|
1237
1184
|
if (issued.length > 0) {
|
|
1238
1185
|
const rows = await this.#state.query({
|
|
@@ -1246,12 +1193,28 @@ var PostgresCompletion = class {
|
|
|
1246
1193
|
}
|
|
1247
1194
|
if (transaction === void 0) {
|
|
1248
1195
|
for (const lease of leases) {
|
|
1249
|
-
this.#state.handedOut.
|
|
1196
|
+
this.#state.handedOut.forget(lease);
|
|
1250
1197
|
}
|
|
1251
1198
|
}
|
|
1252
1199
|
return leases.map((_, ord) => completed.has(ord));
|
|
1253
1200
|
}
|
|
1254
1201
|
};
|
|
1202
|
+
function fenceOf(lease, held) {
|
|
1203
|
+
return [lease.deliveryId, held.lane, held.epoch, held.claims];
|
|
1204
|
+
}
|
|
1205
|
+
function issuedLeases(leases) {
|
|
1206
|
+
const issued = [];
|
|
1207
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1208
|
+
leases.forEach((lease, ord) => {
|
|
1209
|
+
const held = parseLeaseToken(lease.leaseToken);
|
|
1210
|
+
const identity = DeliveryLeaseIdentity.of(lease.deliveryId, lease.leaseToken);
|
|
1211
|
+
if (held !== void 0 && !seen.has(identity)) {
|
|
1212
|
+
seen.add(identity);
|
|
1213
|
+
issued.push({ ord, id: lease.deliveryId, ...held });
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
return issued;
|
|
1217
|
+
}
|
|
1255
1218
|
var OwnedConnections = class {
|
|
1256
1219
|
#clients = /* @__PURE__ */ new Set();
|
|
1257
1220
|
/**
|
|
@@ -1350,6 +1313,24 @@ var PostgresMaintenance = class {
|
|
|
1350
1313
|
/** Implements {@link PostgresEventStore.purge}. */
|
|
1351
1314
|
async purge(request) {
|
|
1352
1315
|
const total = { deletedDeliveries: 0, deletedEvents: 0 };
|
|
1316
|
+
const batches = this.#purgeBatchPlan(request);
|
|
1317
|
+
for (const [sql, cutoff, deleteOrphans] of batches) {
|
|
1318
|
+
if (cutoff === void 0) {
|
|
1319
|
+
continue;
|
|
1320
|
+
}
|
|
1321
|
+
let deletedInBatch = this.#state.purgeBatchSize;
|
|
1322
|
+
while (deletedInBatch === this.#state.purgeBatchSize) {
|
|
1323
|
+
const batch = await this.#purgeBatch(sql, { cutoff, deleteOrphans });
|
|
1324
|
+
deletedInBatch = batch.deletedDeliveries;
|
|
1325
|
+
total.deletedDeliveries += batch.deletedDeliveries;
|
|
1326
|
+
total.deletedEvents += batch.deletedEvents;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
total.deletedEvents += await this.#purgeAllOrphanEvents(request);
|
|
1330
|
+
return total;
|
|
1331
|
+
}
|
|
1332
|
+
/** The purge statements to run in order: SQL, cutoff (undefined skips it) and the orphan-event delete. */
|
|
1333
|
+
#purgeBatchPlan(request) {
|
|
1353
1334
|
const batches = [
|
|
1354
1335
|
[
|
|
1355
1336
|
this.#state.statements.purge.purgeCompletedBatch(),
|
|
@@ -1369,21 +1350,14 @@ var PostgresMaintenance = class {
|
|
|
1369
1350
|
this.#state.statements.purge.deleteOrphanEventsWithoutCompletion
|
|
1370
1351
|
]);
|
|
1371
1352
|
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
while (deletedInBatch === this.#state.purgeBatchSize) {
|
|
1378
|
-
const batch = await this.#purgeBatch(sql, { cutoff, deleteOrphans });
|
|
1379
|
-
deletedInBatch = batch.deletedDeliveries;
|
|
1380
|
-
total.deletedDeliveries += batch.deletedDeliveries;
|
|
1381
|
-
total.deletedEvents += batch.deletedEvents;
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1353
|
+
return batches;
|
|
1354
|
+
}
|
|
1355
|
+
/** Removes the events left without deliveries, in two passes; returns how many events were deleted. */
|
|
1356
|
+
async #purgeAllOrphanEvents(request) {
|
|
1357
|
+
let deletedEvents = 0;
|
|
1384
1358
|
const orphanCutoff = [request.completedBefore, request.deadBefore].filter((cutoff) => cutoff !== void 0).sort((first, second) => first.getTime() - second.getTime())[0];
|
|
1385
1359
|
if (orphanCutoff !== void 0) {
|
|
1386
|
-
|
|
1360
|
+
deletedEvents += await this.#purgeOrphanEvents(
|
|
1387
1361
|
this.#state.statements.purge.lockOrphanEventsBatch,
|
|
1388
1362
|
{
|
|
1389
1363
|
deleteStatement: this.#state.statements.purge.deleteOrphanEventsWithoutCompletion,
|
|
@@ -1393,7 +1367,7 @@ var PostgresMaintenance = class {
|
|
|
1393
1367
|
);
|
|
1394
1368
|
}
|
|
1395
1369
|
if (request.completedBefore !== void 0) {
|
|
1396
|
-
|
|
1370
|
+
deletedEvents += await this.#purgeOrphanEvents(
|
|
1397
1371
|
this.#state.statements.purge.lockCompletedOrphanEventsBatch,
|
|
1398
1372
|
{
|
|
1399
1373
|
deleteStatement: this.#state.statements.purge.deleteExpiredCompletedOrphanEvents,
|
|
@@ -1402,7 +1376,7 @@ var PostgresMaintenance = class {
|
|
|
1402
1376
|
}
|
|
1403
1377
|
);
|
|
1404
1378
|
}
|
|
1405
|
-
return
|
|
1379
|
+
return deletedEvents;
|
|
1406
1380
|
}
|
|
1407
1381
|
async #purgeOrphanEvents(lockStatement, {
|
|
1408
1382
|
deleteStatement,
|
|
@@ -1457,6 +1431,37 @@ var PostgresMaintenance = class {
|
|
|
1457
1431
|
});
|
|
1458
1432
|
}
|
|
1459
1433
|
};
|
|
1434
|
+
var WAKE_UPS = ["notify", "poll"];
|
|
1435
|
+
var DEFAULT_PURGE_BATCH_SIZE = 1e3;
|
|
1436
|
+
function resolveStoreSettings(options) {
|
|
1437
|
+
const sources = [options.pool, options.connectionString, options.executor].filter(
|
|
1438
|
+
(source) => source !== void 0
|
|
1439
|
+
);
|
|
1440
|
+
if (sources.length !== 1) {
|
|
1441
|
+
throw new ConfigurationError(
|
|
1442
|
+
"PostgresEventStore needs exactly one of pool, connectionString or executor"
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1445
|
+
const purgeBatchSize = PositiveInteger.of(
|
|
1446
|
+
options.purgeBatchSize ?? DEFAULT_PURGE_BATCH_SIZE,
|
|
1447
|
+
"purgeBatchSize"
|
|
1448
|
+
);
|
|
1449
|
+
const wakeUp = options.wakeUp ?? "notify";
|
|
1450
|
+
if (!WAKE_UPS.includes(wakeUp)) {
|
|
1451
|
+
throw new ConfigurationError(`wakeUp must be 'notify' or 'poll', received ${String(wakeUp)}`);
|
|
1452
|
+
}
|
|
1453
|
+
const preparedStatements = options.preparedStatements ?? true;
|
|
1454
|
+
if (typeof preparedStatements !== "boolean") {
|
|
1455
|
+
throw new ConfigurationError(
|
|
1456
|
+
`preparedStatements must be a boolean, received ${String(preparedStatements)}`
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1459
|
+
return {
|
|
1460
|
+
purgeBatchSize,
|
|
1461
|
+
wakeUp,
|
|
1462
|
+
queryOptions: preparedStatements ? { prepare: true } : void 0
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1460
1465
|
|
|
1461
1466
|
// src/statements-append.ts
|
|
1462
1467
|
var AppendStatements = class {
|
|
@@ -1474,15 +1479,27 @@ var AppendStatements = class {
|
|
|
1474
1479
|
* inserted. Deliveries of events that already existed are left to {@link insertDeliveries}.
|
|
1475
1480
|
*/
|
|
1476
1481
|
get insertFreshEvents() {
|
|
1477
|
-
|
|
1478
|
-
|
|
1482
|
+
return `WITH ${this.#reservedSequences()}, ${this.#incomingEvents()}, ${this.#insertedEvents()}, ${this.#insertedDeliveries()}, ${this.#wakeUp("inserted_deliveries", "$5")}
|
|
1483
|
+
SELECT incoming.id, incoming.sequence::text AS sequence,
|
|
1484
|
+
incoming.id IN (SELECT inserted_events.id FROM inserted_events) AS inserted,
|
|
1485
|
+
(SELECT count(*) FROM inserted_deliveries)::int AS deliveries
|
|
1486
|
+
FROM incoming
|
|
1487
|
+
WHERE (SELECT count(*) FROM woken) >= 0`;
|
|
1488
|
+
}
|
|
1489
|
+
/** The CTE `reserved` of {@link insertFreshEvents}: the sequences the keys of `$4` reserve. */
|
|
1490
|
+
#reservedSequences() {
|
|
1491
|
+
return `reserved AS (
|
|
1479
1492
|
INSERT INTO ${this.#names.keySequences} AS reserved (key, sequence)
|
|
1480
1493
|
SELECT requested.key, requested.count
|
|
1481
1494
|
FROM jsonb_to_recordset($4::jsonb) AS requested (key text, count bigint)
|
|
1482
1495
|
ORDER BY requested.key
|
|
1483
1496
|
ON CONFLICT (key) DO UPDATE SET sequence = reserved.sequence + EXCLUDED.sequence
|
|
1484
1497
|
RETURNING reserved.key AS key, reserved.sequence AS sequence
|
|
1485
|
-
)
|
|
1498
|
+
)`;
|
|
1499
|
+
}
|
|
1500
|
+
/** The CTE `incoming` of {@link insertFreshEvents}: the events of `$1` with their key sequences. */
|
|
1501
|
+
#incomingEvents() {
|
|
1502
|
+
return `incoming AS (
|
|
1486
1503
|
SELECT incoming.*,
|
|
1487
1504
|
CASE WHEN incoming.key IS NULL THEN NULL ELSE
|
|
1488
1505
|
reserved.sequence - count(*) OVER (PARTITION BY incoming.key)
|
|
@@ -1492,8 +1509,12 @@ var AppendStatements = class {
|
|
|
1492
1509
|
ordinal int, id text, type text, source text, key text, payload jsonb, headers jsonb,
|
|
1493
1510
|
correlation_id text, causation_id text, occurred_at timestamptz, handlers text[])
|
|
1494
1511
|
LEFT JOIN reserved ON reserved.key = incoming.key
|
|
1495
|
-
)
|
|
1496
|
-
|
|
1512
|
+
)`;
|
|
1513
|
+
}
|
|
1514
|
+
/** The CTE `inserted_events` of {@link insertFreshEvents}: the events of `$1` that do not exist yet. */
|
|
1515
|
+
#insertedEvents() {
|
|
1516
|
+
return `inserted_events AS (
|
|
1517
|
+
INSERT INTO ${this.#names.events}
|
|
1497
1518
|
(id, type, source, key, sequence, payload, headers, correlation_id, causation_id, occurred_at, created_at, handlers)
|
|
1498
1519
|
SELECT incoming.id, incoming.type, incoming.source, incoming.key, incoming.sequence,
|
|
1499
1520
|
COALESCE(incoming.payload, 'null'::jsonb), COALESCE(incoming.headers, '{}'::jsonb),
|
|
@@ -1503,7 +1524,11 @@ var AppendStatements = class {
|
|
|
1503
1524
|
ORDER BY incoming.id
|
|
1504
1525
|
ON CONFLICT (id) DO NOTHING
|
|
1505
1526
|
RETURNING id, key, sequence
|
|
1506
|
-
)
|
|
1527
|
+
)`;
|
|
1528
|
+
}
|
|
1529
|
+
/** The CTE `inserted_deliveries` of {@link insertFreshEvents}: the deliveries of `$3` of the inserted events. */
|
|
1530
|
+
#insertedDeliveries() {
|
|
1531
|
+
return `inserted_deliveries AS (
|
|
1507
1532
|
INSERT INTO ${this.#names.deliveries}
|
|
1508
1533
|
(id, event_id, handler, lane, status, attempts, available_at, key, sequence, created_at, updated_at)
|
|
1509
1534
|
SELECT delivery.id, delivery.event_id, delivery.handler, ${laneExpression("event.key", "event.id")}, 'pending', 0,
|
|
@@ -1513,12 +1538,7 @@ var AppendStatements = class {
|
|
|
1513
1538
|
ORDER BY delivery.event_id, delivery.handler
|
|
1514
1539
|
ON CONFLICT (event_id, handler) DO NOTHING
|
|
1515
1540
|
RETURNING available_at
|
|
1516
|
-
)
|
|
1517
|
-
SELECT incoming.id, incoming.sequence::text AS sequence,
|
|
1518
|
-
incoming.id IN (SELECT inserted_events.id FROM inserted_events) AS inserted,
|
|
1519
|
-
(SELECT count(*) FROM inserted_deliveries)::int AS deliveries
|
|
1520
|
-
FROM incoming
|
|
1521
|
-
WHERE (SELECT count(*) FROM woken) >= 0`;
|
|
1541
|
+
)`;
|
|
1522
1542
|
}
|
|
1523
1543
|
/** One wake-up notification on {@link PostgresNames.notificationChannel}, outside any publishing transaction. */
|
|
1524
1544
|
get notify() {
|
|
@@ -2045,11 +2065,146 @@ SELECT count(*)::int AS count FROM changed`;
|
|
|
2045
2065
|
}
|
|
2046
2066
|
};
|
|
2047
2067
|
|
|
2048
|
-
// src/statements-
|
|
2049
|
-
var FREE_LANE_SURPLUS = 16;
|
|
2068
|
+
// src/statements-candidates.ts
|
|
2050
2069
|
function notRunning(running, alias) {
|
|
2051
2070
|
return running === null ? "true" : `NOT EXISTS (SELECT 1 FROM ${running} AS running WHERE running.handler = ${alias}.handler AND running.key = ${alias}.key)`;
|
|
2052
2071
|
}
|
|
2072
|
+
var CandidateStatements = class {
|
|
2073
|
+
#names;
|
|
2074
|
+
constructor(names) {
|
|
2075
|
+
this.#names = names;
|
|
2076
|
+
}
|
|
2077
|
+
/**
|
|
2078
|
+
* CTEs that pick up to `limit` claimable deliveries from the lanes of the CTE `scope` (columns `lane`,
|
|
2079
|
+
* `epoch`, `in_flight`), named after `prefix`; the result is `<prefix>_candidates (id, lane, available_at)`.
|
|
2080
|
+
* The rules are those of `ClaimStatements.claim`. The fast window reads the first {@link LANE_SCAN_WINDOW} due
|
|
2081
|
+
* deliveries of each lane (at most {@link CLAIM_SCAN_WINDOW} together); of a key, only the lowest sequence
|
|
2082
|
+
* in the window can be its head, so only that one is checked for an earlier pending delivery. Only when
|
|
2083
|
+
* that yields fewer than `limit` are the lanes whose window filled up looked at again with a loose index
|
|
2084
|
+
* scan over the heads of all their keys. `running` names the CTE of the keyed deliveries running in the
|
|
2085
|
+
* lanes, or is null when nothing runs there.
|
|
2086
|
+
*/
|
|
2087
|
+
candidates(prefix, {
|
|
2088
|
+
scope,
|
|
2089
|
+
running,
|
|
2090
|
+
limit
|
|
2091
|
+
}) {
|
|
2092
|
+
const spec = { prefix, scope, running, limit };
|
|
2093
|
+
return this.#fastCandidates(spec) + this.#slowCandidates(spec);
|
|
2094
|
+
}
|
|
2095
|
+
/** The fast window of each lane and the claimable deliveries in it (`<prefix>_window`, `<prefix>_fast`). */
|
|
2096
|
+
#fastCandidates({ prefix, scope, running, limit }) {
|
|
2097
|
+
const deliveries = this.#names.deliveries;
|
|
2098
|
+
return `${prefix}_window AS MATERIALIZED (
|
|
2099
|
+
SELECT recent.id, recent.lane, recent.available_at, recent.handler, recent.key, recent.sequence
|
|
2100
|
+
FROM ${scope}
|
|
2101
|
+
CROSS JOIN LATERAL (
|
|
2102
|
+
SELECT delivery.id, delivery.lane, delivery.available_at, delivery.handler, delivery.key, delivery.sequence
|
|
2103
|
+
FROM ${deliveries} AS delivery
|
|
2104
|
+
WHERE delivery.lane = ${scope}.lane AND delivery.status = 'pending'
|
|
2105
|
+
AND delivery.available_at <= $1::timestamptz AND ${HANDLER_PREDICATE}
|
|
2106
|
+
AND delivery.id <> ALL (${scope}.in_flight)
|
|
2107
|
+
ORDER BY delivery.available_at, delivery.id
|
|
2108
|
+
LIMIT ${LANE_SCAN_WINDOW}
|
|
2109
|
+
) AS recent
|
|
2110
|
+
ORDER BY recent.available_at, recent.id
|
|
2111
|
+
LIMIT ${CLAIM_SCAN_WINDOW}
|
|
2112
|
+
), ${prefix}_fast AS MATERIALIZED (
|
|
2113
|
+
SELECT picked.id, picked.lane, picked.available_at FROM (
|
|
2114
|
+
SELECT unkeyed.id, unkeyed.lane, unkeyed.available_at FROM ${prefix}_window AS unkeyed WHERE unkeyed.key IS NULL
|
|
2115
|
+
UNION ALL
|
|
2116
|
+
SELECT head.id, head.lane, head.available_at
|
|
2117
|
+
FROM (
|
|
2118
|
+
SELECT DISTINCT ON (keyed.handler, keyed.key) keyed.id, keyed.lane, keyed.available_at, keyed.handler, keyed.key, keyed.sequence
|
|
2119
|
+
FROM ${prefix}_window AS keyed
|
|
2120
|
+
WHERE keyed.key IS NOT NULL
|
|
2121
|
+
ORDER BY keyed.handler, keyed.key, keyed.sequence
|
|
2122
|
+
) AS head
|
|
2123
|
+
WHERE NOT EXISTS (
|
|
2124
|
+
SELECT 1 FROM ${deliveries} AS earlier
|
|
2125
|
+
WHERE earlier.lane = head.lane AND earlier.handler = head.handler AND earlier.key = head.key
|
|
2126
|
+
AND earlier.status = 'pending' AND earlier.key IS NOT NULL AND earlier.sequence < head.sequence
|
|
2127
|
+
) AND ${notRunning(running, "head")}
|
|
2128
|
+
) AS picked
|
|
2129
|
+
ORDER BY picked.available_at, picked.id
|
|
2130
|
+
LIMIT ${limit}
|
|
2131
|
+
`;
|
|
2132
|
+
}
|
|
2133
|
+
/**
|
|
2134
|
+
* For lanes whose window filled up while the fast pick came up short: the heads of all their keys and
|
|
2135
|
+
* their unkeyed deliveries, then the union with the fast pick (`<prefix>_candidates`).
|
|
2136
|
+
*/
|
|
2137
|
+
#slowCandidates(spec) {
|
|
2138
|
+
return this.#saturatedLanes(spec) + this.#keyHeads(spec) + this.#keyedHeads(spec) + this.#unkeyedRecent(spec) + this.#unionCandidates(spec);
|
|
2139
|
+
}
|
|
2140
|
+
/** `<prefix>_saturated`: the lanes whose window filled up while the fast pick came up short. */
|
|
2141
|
+
#saturatedLanes({ prefix, scope, limit }) {
|
|
2142
|
+
return `), ${prefix}_saturated AS MATERIALIZED (
|
|
2143
|
+
SELECT ${scope}.lane, ${scope}.in_flight FROM ${scope}
|
|
2144
|
+
WHERE (SELECT count(*) FROM ${prefix}_fast) < ${limit}
|
|
2145
|
+
AND ((SELECT count(*) FROM ${prefix}_window) >= ${CLAIM_SCAN_WINDOW}
|
|
2146
|
+
OR ${scope}.lane IN (
|
|
2147
|
+
SELECT full_lane.lane FROM ${prefix}_window AS full_lane
|
|
2148
|
+
GROUP BY full_lane.lane HAVING count(*) >= ${LANE_SCAN_WINDOW}
|
|
2149
|
+
))
|
|
2150
|
+
`;
|
|
2151
|
+
}
|
|
2152
|
+
/** `<prefix>_heads`: the head of every key of the saturated lanes. */
|
|
2153
|
+
#keyHeads({ prefix }) {
|
|
2154
|
+
return `), ${prefix}_heads AS MATERIALIZED (
|
|
2155
|
+
SELECT head.id, head.lane, head.available_at, head.handler, head.key
|
|
2156
|
+
FROM ${prefix}_saturated AS scope
|
|
2157
|
+
CROSS JOIN LATERAL (${keyHeads(this.#names, "scope")}) AS head
|
|
2158
|
+
`;
|
|
2159
|
+
}
|
|
2160
|
+
/** `<prefix>_keyed`: the due heads that are not in flight and whose key runs nowhere. */
|
|
2161
|
+
#keyedHeads({ prefix, running, limit }) {
|
|
2162
|
+
return `), ${prefix}_keyed AS MATERIALIZED (
|
|
2163
|
+
SELECT heads.id, heads.lane, heads.available_at
|
|
2164
|
+
FROM ${prefix}_heads AS heads
|
|
2165
|
+
JOIN ${prefix}_saturated AS scope ON scope.lane = heads.lane
|
|
2166
|
+
WHERE heads.available_at <= $1::timestamptz AND heads.id <> ALL (scope.in_flight) AND ${notRunning(running, "heads")}
|
|
2167
|
+
ORDER BY heads.available_at, heads.id
|
|
2168
|
+
LIMIT ${limit}
|
|
2169
|
+
`;
|
|
2170
|
+
}
|
|
2171
|
+
/** `<prefix>_unkeyed`: the due unkeyed deliveries of the saturated lanes that are not in flight. */
|
|
2172
|
+
#unkeyedRecent({ prefix, limit }) {
|
|
2173
|
+
const deliveries = this.#names.deliveries;
|
|
2174
|
+
return `), ${prefix}_unkeyed AS MATERIALIZED (
|
|
2175
|
+
SELECT recent.id, recent.lane, recent.available_at
|
|
2176
|
+
FROM ${prefix}_saturated AS scope
|
|
2177
|
+
CROSS JOIN LATERAL (
|
|
2178
|
+
SELECT delivery.id, delivery.lane, delivery.available_at
|
|
2179
|
+
FROM ${deliveries} AS delivery
|
|
2180
|
+
WHERE delivery.lane = scope.lane AND delivery.key IS NULL AND delivery.status = 'pending'
|
|
2181
|
+
AND delivery.available_at <= $1::timestamptz AND ${HANDLER_PREDICATE}
|
|
2182
|
+
AND delivery.id <> ALL (scope.in_flight)
|
|
2183
|
+
ORDER BY delivery.available_at, delivery.id
|
|
2184
|
+
LIMIT ${limit}
|
|
2185
|
+
) AS recent
|
|
2186
|
+
ORDER BY recent.available_at, recent.id
|
|
2187
|
+
LIMIT ${limit}
|
|
2188
|
+
`;
|
|
2189
|
+
}
|
|
2190
|
+
/** `<prefix>_candidates`: the union of the fast pick and the slow picks, oldest first, at most `limit`. */
|
|
2191
|
+
#unionCandidates({ prefix, limit }) {
|
|
2192
|
+
return `), ${prefix}_candidates AS MATERIALIZED (
|
|
2193
|
+
SELECT chosen.id, chosen.lane, chosen.available_at FROM (
|
|
2194
|
+
SELECT fast.id, fast.lane, fast.available_at FROM ${prefix}_fast AS fast
|
|
2195
|
+
UNION
|
|
2196
|
+
SELECT keyed.id, keyed.lane, keyed.available_at FROM ${prefix}_keyed AS keyed
|
|
2197
|
+
UNION
|
|
2198
|
+
SELECT unkeyed.id, unkeyed.lane, unkeyed.available_at FROM ${prefix}_unkeyed AS unkeyed
|
|
2199
|
+
) AS chosen
|
|
2200
|
+
ORDER BY chosen.available_at, chosen.id
|
|
2201
|
+
LIMIT ${limit}
|
|
2202
|
+
)`;
|
|
2203
|
+
}
|
|
2204
|
+
};
|
|
2205
|
+
|
|
2206
|
+
// src/statements-lanes.ts
|
|
2207
|
+
var FREE_LANE_SURPLUS = 16;
|
|
2053
2208
|
var freeLane = (alias) => `(${alias}.lease_expires_at IS NULL OR ${alias}.lease_expires_at <= $1::timestamptz
|
|
2054
2209
|
OR (${alias}.lease_owner = $4::text AND ${alias}.claim_floor < $6::bigint))`;
|
|
2055
2210
|
var NO_FREE_LANES = `), free_lanes AS (
|
|
@@ -2061,8 +2216,10 @@ var NO_FREE_LANES = `), free_lanes AS (
|
|
|
2061
2216
|
)`;
|
|
2062
2217
|
var LaneStatements = class {
|
|
2063
2218
|
#names;
|
|
2219
|
+
#candidateStatements;
|
|
2064
2220
|
constructor(names) {
|
|
2065
2221
|
this.#names = names;
|
|
2222
|
+
this.#candidateStatements = new CandidateStatements(names);
|
|
2066
2223
|
}
|
|
2067
2224
|
/**
|
|
2068
2225
|
* A claim in one statement, the only one in the common case. Parameters:
|
|
@@ -2137,7 +2294,7 @@ var LaneStatements = class {
|
|
|
2137
2294
|
CROSS JOIN LATERAL unnest(held_scope.in_flight) AS flying (id)
|
|
2138
2295
|
JOIN ${deliveries} AS delivery ON delivery.id = flying.id
|
|
2139
2296
|
WHERE delivery.status = 'pending' AND delivery.key IS NOT NULL
|
|
2140
|
-
), ${this.#candidates("held", { scope: "held_scope", running: "held_running", limit: "$3::int" })}, wanted AS MATERIALIZED (
|
|
2297
|
+
), ${this.#candidateStatements.candidates("held", { scope: "held_scope", running: "held_running", limit: "$3::int" })}, wanted AS MATERIALIZED (
|
|
2141
2298
|
SELECT CASE WHEN (SELECT value FROM stale) THEN 0
|
|
2142
2299
|
ELSE $3::int - (SELECT count(*) FROM held_candidates)::int END AS count
|
|
2143
2300
|
`;
|
|
@@ -2226,7 +2383,7 @@ var LaneStatements = class {
|
|
|
2226
2383
|
WHERE locked.epoch = seen.epoch AND locked.in_flight = seen.in_flight
|
|
2227
2384
|
AND locked.lease_owner IS NOT DISTINCT FROM seen.lease_owner
|
|
2228
2385
|
AND locked.lease_expires_at IS NOT DISTINCT FROM seen.lease_expires_at
|
|
2229
|
-
), ${this.#candidates("free", { scope: "free_scope", running: null, limit: "(SELECT count FROM wanted)" })}`;
|
|
2386
|
+
), ${this.#candidateStatements.candidates("free", { scope: "free_scope", running: null, limit: "(SELECT count FROM wanted)" })}`;
|
|
2230
2387
|
}
|
|
2231
2388
|
/** The picked deliveries of held and taken lanes with their events, and the picks per lane. */
|
|
2232
2389
|
#claimPicks() {
|
|
@@ -2315,113 +2472,6 @@ SELECT (SELECT value FROM stale) AS stale,
|
|
|
2315
2472
|
FROM (SELECT 1) AS anchor
|
|
2316
2473
|
LEFT JOIN picked_rows AS picked ON true
|
|
2317
2474
|
ORDER BY picked.due_at, picked.delivery_id`;
|
|
2318
|
-
}
|
|
2319
|
-
/**
|
|
2320
|
-
* CTEs that pick up to `limit` claimable deliveries from the lanes of the CTE `scope` (columns `lane`,
|
|
2321
|
-
* `epoch`, `in_flight`), named after `prefix`; the result is `<prefix>_candidates (id, lane, available_at)`.
|
|
2322
|
-
* The rules are those of `ClaimStatements.claim`. The fast window reads the first {@link LANE_SCAN_WINDOW} due
|
|
2323
|
-
* deliveries of each lane (at most {@link CLAIM_SCAN_WINDOW} together); of a key, only the lowest sequence
|
|
2324
|
-
* in the window can be its head, so only that one is checked for an earlier pending delivery. Only when
|
|
2325
|
-
* that yields fewer than `limit` are the lanes whose window filled up looked at again with a loose index
|
|
2326
|
-
* scan over the heads of all their keys. `running` names the CTE of the keyed deliveries running in the
|
|
2327
|
-
* lanes, or is null when nothing runs there.
|
|
2328
|
-
*/
|
|
2329
|
-
#candidates(prefix, {
|
|
2330
|
-
scope,
|
|
2331
|
-
running,
|
|
2332
|
-
limit
|
|
2333
|
-
}) {
|
|
2334
|
-
const spec = { prefix, scope, running, limit };
|
|
2335
|
-
return this.#fastCandidates(spec) + this.#slowCandidates(spec);
|
|
2336
|
-
}
|
|
2337
|
-
/** The fast window of each lane and the claimable deliveries in it (`<prefix>_window`, `<prefix>_fast`). */
|
|
2338
|
-
#fastCandidates({ prefix, scope, running, limit }) {
|
|
2339
|
-
const deliveries = this.#names.deliveries;
|
|
2340
|
-
return `${prefix}_window AS MATERIALIZED (
|
|
2341
|
-
SELECT recent.id, recent.lane, recent.available_at, recent.handler, recent.key, recent.sequence
|
|
2342
|
-
FROM ${scope}
|
|
2343
|
-
CROSS JOIN LATERAL (
|
|
2344
|
-
SELECT delivery.id, delivery.lane, delivery.available_at, delivery.handler, delivery.key, delivery.sequence
|
|
2345
|
-
FROM ${deliveries} AS delivery
|
|
2346
|
-
WHERE delivery.lane = ${scope}.lane AND delivery.status = 'pending'
|
|
2347
|
-
AND delivery.available_at <= $1::timestamptz AND ${HANDLER_PREDICATE}
|
|
2348
|
-
AND delivery.id <> ALL (${scope}.in_flight)
|
|
2349
|
-
ORDER BY delivery.available_at, delivery.id
|
|
2350
|
-
LIMIT ${LANE_SCAN_WINDOW}
|
|
2351
|
-
) AS recent
|
|
2352
|
-
ORDER BY recent.available_at, recent.id
|
|
2353
|
-
LIMIT ${CLAIM_SCAN_WINDOW}
|
|
2354
|
-
), ${prefix}_fast AS MATERIALIZED (
|
|
2355
|
-
SELECT picked.id, picked.lane, picked.available_at FROM (
|
|
2356
|
-
SELECT unkeyed.id, unkeyed.lane, unkeyed.available_at FROM ${prefix}_window AS unkeyed WHERE unkeyed.key IS NULL
|
|
2357
|
-
UNION ALL
|
|
2358
|
-
SELECT head.id, head.lane, head.available_at
|
|
2359
|
-
FROM (
|
|
2360
|
-
SELECT DISTINCT ON (keyed.handler, keyed.key) keyed.id, keyed.lane, keyed.available_at, keyed.handler, keyed.key, keyed.sequence
|
|
2361
|
-
FROM ${prefix}_window AS keyed
|
|
2362
|
-
WHERE keyed.key IS NOT NULL
|
|
2363
|
-
ORDER BY keyed.handler, keyed.key, keyed.sequence
|
|
2364
|
-
) AS head
|
|
2365
|
-
WHERE NOT EXISTS (
|
|
2366
|
-
SELECT 1 FROM ${deliveries} AS earlier
|
|
2367
|
-
WHERE earlier.lane = head.lane AND earlier.handler = head.handler AND earlier.key = head.key
|
|
2368
|
-
AND earlier.status = 'pending' AND earlier.key IS NOT NULL AND earlier.sequence < head.sequence
|
|
2369
|
-
) AND ${notRunning(running, "head")}
|
|
2370
|
-
) AS picked
|
|
2371
|
-
ORDER BY picked.available_at, picked.id
|
|
2372
|
-
LIMIT ${limit}
|
|
2373
|
-
`;
|
|
2374
|
-
}
|
|
2375
|
-
/**
|
|
2376
|
-
* For lanes whose window filled up while the fast pick came up short: the heads of all their keys and
|
|
2377
|
-
* their unkeyed deliveries, then the union with the fast pick (`<prefix>_candidates`).
|
|
2378
|
-
*/
|
|
2379
|
-
#slowCandidates({ prefix, scope, running, limit }) {
|
|
2380
|
-
const deliveries = this.#names.deliveries;
|
|
2381
|
-
return `), ${prefix}_saturated AS MATERIALIZED (
|
|
2382
|
-
SELECT ${scope}.lane, ${scope}.in_flight FROM ${scope}
|
|
2383
|
-
WHERE (SELECT count(*) FROM ${prefix}_fast) < ${limit}
|
|
2384
|
-
AND ((SELECT count(*) FROM ${prefix}_window) >= ${CLAIM_SCAN_WINDOW}
|
|
2385
|
-
OR ${scope}.lane IN (
|
|
2386
|
-
SELECT full_lane.lane FROM ${prefix}_window AS full_lane
|
|
2387
|
-
GROUP BY full_lane.lane HAVING count(*) >= ${LANE_SCAN_WINDOW}
|
|
2388
|
-
))
|
|
2389
|
-
), ${prefix}_heads AS MATERIALIZED (
|
|
2390
|
-
SELECT head.id, head.lane, head.available_at, head.handler, head.key
|
|
2391
|
-
FROM ${prefix}_saturated AS scope
|
|
2392
|
-
CROSS JOIN LATERAL (${keyHeads(this.#names, "scope")}) AS head
|
|
2393
|
-
), ${prefix}_keyed AS MATERIALIZED (
|
|
2394
|
-
SELECT heads.id, heads.lane, heads.available_at
|
|
2395
|
-
FROM ${prefix}_heads AS heads
|
|
2396
|
-
JOIN ${prefix}_saturated AS scope ON scope.lane = heads.lane
|
|
2397
|
-
WHERE heads.available_at <= $1::timestamptz AND heads.id <> ALL (scope.in_flight) AND ${notRunning(running, "heads")}
|
|
2398
|
-
ORDER BY heads.available_at, heads.id
|
|
2399
|
-
LIMIT ${limit}
|
|
2400
|
-
), ${prefix}_unkeyed AS MATERIALIZED (
|
|
2401
|
-
SELECT recent.id, recent.lane, recent.available_at
|
|
2402
|
-
FROM ${prefix}_saturated AS scope
|
|
2403
|
-
CROSS JOIN LATERAL (
|
|
2404
|
-
SELECT delivery.id, delivery.lane, delivery.available_at
|
|
2405
|
-
FROM ${deliveries} AS delivery
|
|
2406
|
-
WHERE delivery.lane = scope.lane AND delivery.key IS NULL AND delivery.status = 'pending'
|
|
2407
|
-
AND delivery.available_at <= $1::timestamptz AND ${HANDLER_PREDICATE}
|
|
2408
|
-
AND delivery.id <> ALL (scope.in_flight)
|
|
2409
|
-
ORDER BY delivery.available_at, delivery.id
|
|
2410
|
-
LIMIT ${limit}
|
|
2411
|
-
) AS recent
|
|
2412
|
-
ORDER BY recent.available_at, recent.id
|
|
2413
|
-
LIMIT ${limit}
|
|
2414
|
-
), ${prefix}_candidates AS MATERIALIZED (
|
|
2415
|
-
SELECT chosen.id, chosen.lane, chosen.available_at FROM (
|
|
2416
|
-
SELECT fast.id, fast.lane, fast.available_at FROM ${prefix}_fast AS fast
|
|
2417
|
-
UNION
|
|
2418
|
-
SELECT keyed.id, keyed.lane, keyed.available_at FROM ${prefix}_keyed AS keyed
|
|
2419
|
-
UNION
|
|
2420
|
-
SELECT unkeyed.id, unkeyed.lane, unkeyed.available_at FROM ${prefix}_unkeyed AS unkeyed
|
|
2421
|
-
) AS chosen
|
|
2422
|
-
ORDER BY chosen.available_at, chosen.id
|
|
2423
|
-
LIMIT ${limit}
|
|
2424
|
-
)`;
|
|
2425
2475
|
}
|
|
2426
2476
|
/**
|
|
2427
2477
|
* Takes up to `$3` free lanes (no lease, or an expired one) that have a due
|
|
@@ -2808,8 +2858,6 @@ var PostgresStatements = class {
|
|
|
2808
2858
|
};
|
|
2809
2859
|
|
|
2810
2860
|
// src/postgres-event-store.ts
|
|
2811
|
-
var WAKE_UPS = ["notify", "poll"];
|
|
2812
|
-
var DEFAULT_PURGE_BATCH_SIZE = 1e3;
|
|
2813
2861
|
var PostgresEventStore = class {
|
|
2814
2862
|
#state;
|
|
2815
2863
|
#appender;
|
|
@@ -2836,10 +2884,9 @@ var PostgresEventStore = class {
|
|
|
2836
2884
|
queryOptions,
|
|
2837
2885
|
notifies: wakeUp === "notify",
|
|
2838
2886
|
query: ({ sql, parameters, transaction }) => executor.query(sql, parameters, transaction, queryOptions),
|
|
2839
|
-
handedOut:
|
|
2887
|
+
handedOut: new HandedOutLedger(),
|
|
2840
2888
|
unreconciled: /* @__PURE__ */ new Set(),
|
|
2841
2889
|
generations: /* @__PURE__ */ new Map(),
|
|
2842
|
-
ownerQueues: /* @__PURE__ */ new Map(),
|
|
2843
2890
|
drainingOwners: /* @__PURE__ */ new Set(),
|
|
2844
2891
|
ownedPool,
|
|
2845
2892
|
pool,
|
|
@@ -2946,7 +2993,7 @@ var PostgresEventStore = class {
|
|
|
2946
2993
|
return this.#claims.claim(request);
|
|
2947
2994
|
}
|
|
2948
2995
|
extendLease(lease, leaseExpiresAt) {
|
|
2949
|
-
return this.#completion.extendLease(lease, leaseExpiresAt);
|
|
2996
|
+
return Promise.resolve(this.#completion.extendLease({ lease, leaseExpiresAt }));
|
|
2950
2997
|
}
|
|
2951
2998
|
// eslint-disable-next-line relaybus/max-two-params -- EventStore port signature
|
|
2952
2999
|
complete(lease, now, transaction) {
|
|
@@ -2957,15 +3004,24 @@ var PostgresEventStore = class {
|
|
|
2957
3004
|
}
|
|
2958
3005
|
// eslint-disable-next-line relaybus/max-two-params -- EventStore port signature
|
|
2959
3006
|
reschedule(lease, availableAt, error, now) {
|
|
2960
|
-
return
|
|
3007
|
+
return Promise.resolve(
|
|
3008
|
+
this.#completion.changeClaim({
|
|
3009
|
+
lease,
|
|
3010
|
+
change: { kind: "reschedule", availableAt, error, now }
|
|
3011
|
+
})
|
|
3012
|
+
);
|
|
2961
3013
|
}
|
|
2962
3014
|
// eslint-disable-next-line relaybus/max-two-params -- EventStore port signature
|
|
2963
3015
|
bury(lease, error, now) {
|
|
2964
|
-
return
|
|
3016
|
+
return Promise.resolve(
|
|
3017
|
+
this.#completion.changeClaim({ lease, change: { kind: "bury", error, now } })
|
|
3018
|
+
);
|
|
2965
3019
|
}
|
|
2966
3020
|
/** Returns a leased delivery to pending, due now, without counting the attempt. */
|
|
2967
3021
|
release(lease, now) {
|
|
2968
|
-
return
|
|
3022
|
+
return Promise.resolve(
|
|
3023
|
+
this.#completion.changeClaim({ lease, change: { kind: "release", now } })
|
|
3024
|
+
);
|
|
2969
3025
|
}
|
|
2970
3026
|
/**
|
|
2971
3027
|
* Calls `listener` soon after a transaction that published deliveries due at once commits, in any
|
|
@@ -3044,35 +3100,6 @@ var PostgresEventStore = class {
|
|
|
3044
3100
|
}
|
|
3045
3101
|
}
|
|
3046
3102
|
};
|
|
3047
|
-
function resolveStoreSettings(options) {
|
|
3048
|
-
const sources = [options.pool, options.connectionString, options.executor].filter(
|
|
3049
|
-
(source) => source !== void 0
|
|
3050
|
-
);
|
|
3051
|
-
if (sources.length !== 1) {
|
|
3052
|
-
throw new ConfigurationError(
|
|
3053
|
-
"PostgresEventStore needs exactly one of pool, connectionString or executor"
|
|
3054
|
-
);
|
|
3055
|
-
}
|
|
3056
|
-
const purgeBatchSize = assertPositiveInteger(
|
|
3057
|
-
options.purgeBatchSize ?? DEFAULT_PURGE_BATCH_SIZE,
|
|
3058
|
-
"purgeBatchSize"
|
|
3059
|
-
);
|
|
3060
|
-
const wakeUp = options.wakeUp ?? "notify";
|
|
3061
|
-
if (!WAKE_UPS.includes(wakeUp)) {
|
|
3062
|
-
throw new ConfigurationError(`wakeUp must be 'notify' or 'poll', received ${String(wakeUp)}`);
|
|
3063
|
-
}
|
|
3064
|
-
const preparedStatements = options.preparedStatements ?? true;
|
|
3065
|
-
if (typeof preparedStatements !== "boolean") {
|
|
3066
|
-
throw new ConfigurationError(
|
|
3067
|
-
`preparedStatements must be a boolean, received ${String(preparedStatements)}`
|
|
3068
|
-
);
|
|
3069
|
-
}
|
|
3070
|
-
return {
|
|
3071
|
-
purgeBatchSize,
|
|
3072
|
-
wakeUp,
|
|
3073
|
-
queryOptions: preparedStatements ? { prepare: true } : void 0
|
|
3074
|
-
};
|
|
3075
|
-
}
|
|
3076
3103
|
function connect(options, { logger, owned }) {
|
|
3077
3104
|
if (options.executor !== void 0) {
|
|
3078
3105
|
return { executor: options.executor, pool: void 0, ownedPool: void 0 };
|