pg-boss 12.33.6 → 12.33.7
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/plans.js +2 -2
- package/dist/timekeeper.d.ts +7 -3
- package/dist/timekeeper.d.ts.map +1 -1
- package/dist/timekeeper.js +32 -33
- package/package.json +1 -1
package/dist/plans.js
CHANGED
|
@@ -1961,8 +1961,8 @@ export function insertJobs(schema, { table, name, returnId = true, notify = fals
|
|
|
1961
1961
|
// When notify is enabled we always RETURN start_after so the wrapper below can gate
|
|
1962
1962
|
// the NOTIFY on immediate availability, regardless of whether the caller wants ids.
|
|
1963
1963
|
const returning = notify ? 'RETURNING id, start_after' : returnId ? 'RETURNING id' : '';
|
|
1964
|
-
// A caller that knows the slot names it outright: the cron pass files
|
|
1965
|
-
//
|
|
1964
|
+
// A caller that knows the slot names it outright: the cron pass files an occurrence in the slot
|
|
1965
|
+
// the occurrence falls in, and an offset off now() cannot pin that, since now() here is
|
|
1966
1966
|
// insert time. Only in the statement the pass asks for, because insert() stringifies caller
|
|
1967
1967
|
// objects straight into the recordset below, so a column declared for everyone would be a live,
|
|
1968
1968
|
// undeclared and unvalidated option on the public path, where a bad value surfaces as a raw
|
package/dist/timekeeper.d.ts
CHANGED
|
@@ -127,9 +127,13 @@ declare class Timekeeper extends EventEmitter implements types.EventsMixin {
|
|
|
127
127
|
* job is what keeps the passes that follow from sending it a second time.
|
|
128
128
|
*
|
|
129
129
|
* The window rather than its most recent point, since a rule can put two occurrences inside it
|
|
130
|
-
* and a read that answers with one of them drops the other. A cron expression
|
|
131
|
-
*
|
|
132
|
-
*
|
|
130
|
+
* and a read that answers with one of them drops the other. A cron expression needs only the
|
|
131
|
+
* most recent one, although two of its occurrences either side of a minute fall in slots of
|
|
132
|
+
* their own: the minutes an expression matches are a minute apart at the closest, and passes run
|
|
133
|
+
* closer together than the window is wide, so one lands while a matching minute's first
|
|
134
|
+
* occurrence is still due and before any later minute has come round. It reads an occurrence of
|
|
135
|
+
* that minute and files the job under it. An occurrence this read passes over falls in a minute
|
|
136
|
+
* a pass before it answered for.
|
|
133
137
|
*/
|
|
134
138
|
private readOccurrences;
|
|
135
139
|
private reportSendItError;
|
package/dist/timekeeper.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timekeeper.d.ts","sourceRoot":"","sources":["../src/timekeeper.ts"],"names":[],"mappings":"AAEA,OAAO,YAAY,MAAM,aAAa,CAAA;AAItC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAKvC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AAGnC,eAAO,MAAM,MAAM;;CAElB,CAAA;AAwBD,kGAAkG;AAClG,eAAO,MAAM,iBAAiB,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"timekeeper.d.ts","sourceRoot":"","sources":["../src/timekeeper.ts"],"names":[],"mappings":"AAEA,OAAO,YAAY,MAAM,aAAa,CAAA;AAItC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAA;AAKvC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AAGnC,eAAO,MAAM,MAAM;;CAElB,CAAA;AAwBD,kGAAkG;AAClG,eAAO,MAAM,iBAAiB,OAAO,CAAA;AA8MrC,cAAM,UAAW,SAAQ,YAAa,YAAW,KAAK,CAAC,WAAW;IAChE,EAAE,EAAE,KAAK,CAAC,SAAS,CAAA;IACnB,MAAM,EAAE,KAAK,CAAC,0BAA0B,CAAA;IACxC,OAAO,EAAE,OAAO,CAAA;IAEhB,OAAO,CAAC,OAAO,CAAO;IACtB,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,CAAC,gBAAgB,CAAqC;IAC7D,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,aAAa,CAAQ;IAS7B,OAAO,CAAC,eAAe,CAAoB;IAE3C,SAAS,SAAI;IACb,MAAM;;;;MAAS;gBAEF,EAAE,EAAE,KAAK,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,0BAA0B;IAQ5F,IAAI,YAAY,IAAK,OAAO,CAE3B;IAMD,OAAO,KAAK,YAAY,GAEvB;IAED,OAAO,KAAK,cAAc,GASzB;IAEK,KAAK;IA4BL,IAAI;IAmCV,OAAO,CAAC,mBAAmB;IAUrB,cAAc;IA8Cd,MAAM;IAgDZ;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,eAAe;IAQvB;;;;;OAKG;IACG,IAAI,CAAE,WAAW,GAAE,OAAc;IAoIvC,YAAY,CAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,GAAE,KAAK,CAAC,YAAwC;IAIlG;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,cAAc;IAgBtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,OAAO,CAAC,gBAAgB;IAgBxB;;;;;;;OAOG;IACH,OAAO,CAAC,sBAAsB;IAgB9B;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,eAAe;IAwBvB,OAAO,CAAC,iBAAiB;YAQX,QAAQ;YAsDR,aAAa;IAerB,YAAY,CAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;IAiBrE,WAAW,CAAE,IAAI,EAAE,MAAM,EAAE,GAAG,SAAK,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;IAe1E;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IAYxB;;;;;;;;;;;;;;;;OAgBG;IACH,eAAe,CAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,KAAK,CAAC,sBAA2B,GAAG,IAAI,EAAE;IA0C5E,QAAQ,CAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,GAAE,KAAK,CAAC,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6CzG,UAAU,CAAE,IAAI,EAAE,MAAM,EAAE,GAAG,SAAK,GAAG,OAAO,CAAC,IAAI,CAAC;CAIzD;AAED,eAAe,UAAU,CAAA"}
|
package/dist/timekeeper.js
CHANGED
|
@@ -469,46 +469,41 @@ class Timekeeper extends EventEmitter {
|
|
|
469
469
|
// The payload carries the schedule's key beside its queue name, so the send-it handler knows
|
|
470
470
|
// which row an occurrence came from and can record the job it produced.
|
|
471
471
|
const forwarded = { data: { name, key, data, options }, singletonKey: occurrenceKey(name, key) };
|
|
472
|
-
//
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
//
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
// boundary
|
|
472
|
+
// An occurrence can fall anywhere in the minute, a rule wherever it says and a cron
|
|
473
|
+
// expression on the second its sixth placeholder names, and a slot measured from insert
|
|
474
|
+
// time would then straddle it: two passes on either side of a slot boundary both find the occurrence
|
|
475
|
+
// inside the window and file it in a slot of their own, sending it twice. So an occurrence
|
|
476
|
+
// names the slot it falls in outright. An offset from the insert's own now() would not pin
|
|
477
|
+
// it: everything between reading the clock here and the insert committing counts towards
|
|
478
|
+
// the shifted instant, which lands in the next slot whenever that adds up to a boundary
|
|
479
|
+
// crossing.
|
|
480
|
+
//
|
|
481
|
+
// An instance on a release that files a cron occurrence from insert time agrees with this
|
|
482
|
+
// slot wherever that insert lands in the occurrence's own minute, and disagrees only where
|
|
483
|
+
// it would already have sent the occurrence twice, so a rolling upgrade sends no more jobs
|
|
484
|
+
// than the older release does alone.
|
|
479
485
|
//
|
|
480
486
|
// A missed occurrence names its slot for that reason and one more: it is older than the
|
|
481
487
|
// window, so a slot off insert time would file it in the slot the pass runs in, where it
|
|
482
|
-
// would collide with the
|
|
483
|
-
// than the one insert time computes, so it cannot.
|
|
488
|
+
// would collide with the job of the occurrence due now and be dropped. The slot it names is
|
|
489
|
+
// older than the one insert time computes, so it cannot.
|
|
484
490
|
//
|
|
485
491
|
// One job per slot rather than one per occurrence, which is the resolution the docs promise:
|
|
486
|
-
//
|
|
487
|
-
// fall in slots of their own each send.
|
|
492
|
+
// an expression finer than a slot sends a job a slot, and two occurrences inside one window
|
|
493
|
+
// that fall in slots of their own each send.
|
|
488
494
|
const slots = new Set();
|
|
489
495
|
if (missed !== null) {
|
|
490
496
|
slots.add(throttleSlot(missed));
|
|
491
497
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
slots.add(throttleSlot(occurrence));
|
|
498
|
-
}
|
|
498
|
+
// Through the set the missed occurrence went through, since the window's lower bound falls
|
|
499
|
+
// inside a slot rather than on one: an occurrence on the bound is missed, one a millisecond
|
|
500
|
+
// later is due, and both belong to the same slot and so to the same job.
|
|
501
|
+
for (const occurrence of due.occurrences) {
|
|
502
|
+
slots.add(throttleSlot(occurrence));
|
|
499
503
|
}
|
|
500
504
|
for (const slot of slots) {
|
|
501
505
|
scheduled.push({ ...forwarded, data: { ...forwarded.data, slot }, __singletonSlot: slot });
|
|
502
506
|
}
|
|
503
|
-
// Anything not read as a rule is read as cron, which is what a row carrying no kind at all
|
|
504
|
-
// means: the column defaults to cron, and a reader that cannot see it reads the row the way
|
|
505
|
-
// every release before the column did.
|
|
506
|
-
if (due.kind !== plans.SCHEDULE_KINDS.rrule && due.occurrences.length > 0) {
|
|
507
|
-
// A cron occurrence keeps the slot every release has always filed it in, since an instance
|
|
508
|
-
// still running an older one during a rolling upgrade computes that slot and nothing else,
|
|
509
|
-
// and a slot the two disagree on collapses nothing.
|
|
510
|
-
scheduled.push({ ...forwarded, singletonSeconds: OCCURRENCE_WINDOW_SECONDS });
|
|
511
|
-
}
|
|
512
507
|
}
|
|
513
508
|
this.warnedSchedules = stillBroken;
|
|
514
509
|
if (scheduled.length > 0 && !this.stopped) {
|
|
@@ -624,9 +619,13 @@ class Timekeeper extends EventEmitter {
|
|
|
624
619
|
* job is what keeps the passes that follow from sending it a second time.
|
|
625
620
|
*
|
|
626
621
|
* The window rather than its most recent point, since a rule can put two occurrences inside it
|
|
627
|
-
* and a read that answers with one of them drops the other. A cron expression
|
|
628
|
-
*
|
|
629
|
-
*
|
|
622
|
+
* and a read that answers with one of them drops the other. A cron expression needs only the
|
|
623
|
+
* most recent one, although two of its occurrences either side of a minute fall in slots of
|
|
624
|
+
* their own: the minutes an expression matches are a minute apart at the closest, and passes run
|
|
625
|
+
* closer together than the window is wide, so one lands while a matching minute's first
|
|
626
|
+
* occurrence is still due and before any later minute has come round. It reads an occurrence of
|
|
627
|
+
* that minute and files the job under it. An occurrence this read passes over falls in a minute
|
|
628
|
+
* a pass before it answered for.
|
|
630
629
|
*/
|
|
631
630
|
readOccurrences(expression, kind, tz, databaseTime) {
|
|
632
631
|
const window = new Date(databaseTime - OCCURRENCE_WINDOW_SECONDS * 1000);
|
|
@@ -684,9 +683,9 @@ class Timekeeper extends EventEmitter {
|
|
|
684
683
|
continue;
|
|
685
684
|
}
|
|
686
685
|
const id = JSON.stringify([name, key]);
|
|
687
|
-
// A cron occurrence in the due window names no slot, since
|
|
688
|
-
// clock. That slot is the one the pass
|
|
689
|
-
// catch-up occurrence can name, so the current one stands in for it.
|
|
686
|
+
// A cron occurrence in the due window written by an older instance names no slot, since that
|
|
687
|
+
// insert filed it from its own clock. That slot is the one the pass was running in, which is
|
|
688
|
+
// later than every slot a catch-up occurrence can name, so the current one stands in for it.
|
|
690
689
|
const filed = slot ?? throttleSlot(new Date(this.databaseTime));
|
|
691
690
|
const latest = fired.get(id);
|
|
692
691
|
if (latest === undefined || latest.slot <= filed) {
|