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