@monque/core 1.5.0 → 1.5.2
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/CHANGELOG.md +12 -0
- package/dist/index.cjs +29 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +28 -40
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/scheduler/monque.ts +5 -3
- package/src/scheduler/services/change-stream-handler.ts +43 -24
package/dist/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @monque/core
|
|
2
2
|
|
|
3
|
+
## 1.5.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#227](https://github.com/ueberBrot/monque/pull/227) [`e9208ca`](https://github.com/ueberBrot/monque/commit/e9208ca5c985d84d023161560d5d3ba195394fe1) Thanks [@ueberBrot](https://github.com/ueberBrot)! - Prevent change stream reconnection attempts from running after the scheduler stops. This clears pending reconnect timers during shutdown and adds coverage for the stop-during-backoff scenario.
|
|
8
|
+
|
|
9
|
+
## 1.5.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [#211](https://github.com/ueberBrot/monque/pull/211) [`7181215`](https://github.com/ueberBrot/monque/commit/7181215abac2b5cd231c63f10bf718f0314cc09f) Thanks [@ueberBrot](https://github.com/ueberBrot)! - Close shutdown race condition window by stopping timers before setting isRunning flag
|
|
14
|
+
|
|
3
15
|
## 1.5.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value:
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
let mongodb = require("mongodb");
|
|
3
3
|
let cron_parser = require("cron-parser");
|
|
4
4
|
let node_crypto = require("node:crypto");
|
|
5
5
|
let node_events = require("node:events");
|
|
6
|
-
|
|
7
6
|
//#region src/jobs/document-to-persisted-job.ts
|
|
8
7
|
/**
|
|
9
8
|
* Convert a raw MongoDB document to a strongly-typed {@link PersistedJob}.
|
|
@@ -36,7 +35,6 @@ function documentToPersistedJob(doc) {
|
|
|
36
35
|
if (doc["uniqueKey"] !== void 0) job.uniqueKey = doc["uniqueKey"];
|
|
37
36
|
return job;
|
|
38
37
|
}
|
|
39
|
-
|
|
40
38
|
//#endregion
|
|
41
39
|
//#region src/jobs/types.ts
|
|
42
40
|
/**
|
|
@@ -75,7 +73,6 @@ const CursorDirection = {
|
|
|
75
73
|
FORWARD: "forward",
|
|
76
74
|
BACKWARD: "backward"
|
|
77
75
|
};
|
|
78
|
-
|
|
79
76
|
//#endregion
|
|
80
77
|
//#region src/jobs/guards.ts
|
|
81
78
|
/**
|
|
@@ -288,7 +285,6 @@ function isCancelledJob(job) {
|
|
|
288
285
|
function isRecurringJob(job) {
|
|
289
286
|
return job.repeatInterval !== void 0 && job.repeatInterval !== null;
|
|
290
287
|
}
|
|
291
|
-
|
|
292
288
|
//#endregion
|
|
293
289
|
//#region src/shared/errors.ts
|
|
294
290
|
/**
|
|
@@ -506,7 +502,6 @@ var PayloadTooLargeError = class PayloadTooLargeError extends MonqueError {
|
|
|
506
502
|
if (Error.captureStackTrace) Error.captureStackTrace(this, PayloadTooLargeError);
|
|
507
503
|
}
|
|
508
504
|
};
|
|
509
|
-
|
|
510
505
|
//#endregion
|
|
511
506
|
//#region src/shared/utils/backoff.ts
|
|
512
507
|
/**
|
|
@@ -548,7 +543,7 @@ const DEFAULT_MAX_BACKOFF_DELAY = 1440 * 60 * 1e3;
|
|
|
548
543
|
* ```
|
|
549
544
|
*/
|
|
550
545
|
function calculateBackoff(failCount, baseInterval = DEFAULT_BASE_INTERVAL, maxDelay) {
|
|
551
|
-
const effectiveMaxDelay = maxDelay ??
|
|
546
|
+
const effectiveMaxDelay = maxDelay ?? 864e5;
|
|
552
547
|
let delay = 2 ** failCount * baseInterval;
|
|
553
548
|
if (delay > effectiveMaxDelay) delay = effectiveMaxDelay;
|
|
554
549
|
return new Date(Date.now() + delay);
|
|
@@ -562,12 +557,11 @@ function calculateBackoff(failCount, baseInterval = DEFAULT_BASE_INTERVAL, maxDe
|
|
|
562
557
|
* @returns The delay in milliseconds
|
|
563
558
|
*/
|
|
564
559
|
function calculateBackoffDelay(failCount, baseInterval = DEFAULT_BASE_INTERVAL, maxDelay) {
|
|
565
|
-
const effectiveMaxDelay = maxDelay ??
|
|
560
|
+
const effectiveMaxDelay = maxDelay ?? 864e5;
|
|
566
561
|
let delay = 2 ** failCount * baseInterval;
|
|
567
562
|
if (delay > effectiveMaxDelay) delay = effectiveMaxDelay;
|
|
568
563
|
return delay;
|
|
569
564
|
}
|
|
570
|
-
|
|
571
565
|
//#endregion
|
|
572
566
|
//#region src/shared/utils/cron.ts
|
|
573
567
|
/**
|
|
@@ -621,7 +615,6 @@ function validateCronExpression(expression) {
|
|
|
621
615
|
function handleCronParseError(expression, error) {
|
|
622
616
|
throw new InvalidCronError(expression, `Invalid cron expression "${expression}": ${error instanceof Error ? error.message : "Unknown parsing error"}. Expected 5-field format: "minute hour day-of-month month day-of-week" or predefined expression (e.g. @daily). Example: "0 9 * * 1" (every Monday at 9am)`);
|
|
623
617
|
}
|
|
624
|
-
|
|
625
618
|
//#endregion
|
|
626
619
|
//#region src/shared/utils/error.ts
|
|
627
620
|
/**
|
|
@@ -654,7 +647,6 @@ function toError(value) {
|
|
|
654
647
|
return /* @__PURE__ */ new Error(`Unserializable value (${detail})`);
|
|
655
648
|
}
|
|
656
649
|
}
|
|
657
|
-
|
|
658
650
|
//#endregion
|
|
659
651
|
//#region src/scheduler/helpers.ts
|
|
660
652
|
/**
|
|
@@ -720,7 +712,6 @@ function decodeCursor(cursor) {
|
|
|
720
712
|
throw new InvalidCursorError("Invalid cursor payload");
|
|
721
713
|
}
|
|
722
714
|
}
|
|
723
|
-
|
|
724
715
|
//#endregion
|
|
725
716
|
//#region src/scheduler/services/change-stream-handler.ts
|
|
726
717
|
/**
|
|
@@ -764,6 +755,7 @@ var ChangeStreamHandler = class {
|
|
|
764
755
|
*/
|
|
765
756
|
setup() {
|
|
766
757
|
if (!this.ctx.isRunning()) return;
|
|
758
|
+
this.clearReconnectTimer();
|
|
767
759
|
try {
|
|
768
760
|
this.changeStream = this.ctx.collection.watch([{ $match: { $or: [{ operationType: "insert" }, {
|
|
769
761
|
operationType: "update",
|
|
@@ -823,30 +815,35 @@ var ChangeStreamHandler = class {
|
|
|
823
815
|
this.reconnectAttempts++;
|
|
824
816
|
if (this.reconnectAttempts > this.maxReconnectAttempts) {
|
|
825
817
|
this.usingChangeStreams = false;
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
this.reconnectTimer = null;
|
|
829
|
-
}
|
|
830
|
-
if (this.changeStream) {
|
|
831
|
-
this.changeStream.close().catch(() => {});
|
|
832
|
-
this.changeStream = null;
|
|
833
|
-
}
|
|
818
|
+
this.clearReconnectTimer();
|
|
819
|
+
this.closeChangeStream();
|
|
834
820
|
this.ctx.emit("changestream:fallback", { reason: `Exhausted ${this.maxReconnectAttempts} reconnection attempts: ${error.message}` });
|
|
835
821
|
return;
|
|
836
822
|
}
|
|
837
823
|
const delay = 2 ** (this.reconnectAttempts - 1) * 1e3;
|
|
838
|
-
|
|
824
|
+
this.clearReconnectTimer();
|
|
825
|
+
if (!this.ctx.isRunning()) return;
|
|
839
826
|
this.reconnectTimer = setTimeout(() => {
|
|
840
|
-
this.
|
|
841
|
-
|
|
842
|
-
if (this.changeStream) {
|
|
843
|
-
this.changeStream.close().catch(() => {});
|
|
844
|
-
this.changeStream = null;
|
|
845
|
-
}
|
|
846
|
-
this.setup();
|
|
847
|
-
}
|
|
827
|
+
this.clearReconnectTimer();
|
|
828
|
+
this.reconnect();
|
|
848
829
|
}, delay);
|
|
849
830
|
}
|
|
831
|
+
reconnect() {
|
|
832
|
+
if (!this.ctx.isRunning()) return;
|
|
833
|
+
this.closeChangeStream();
|
|
834
|
+
if (!this.ctx.isRunning()) return;
|
|
835
|
+
this.setup();
|
|
836
|
+
}
|
|
837
|
+
clearReconnectTimer() {
|
|
838
|
+
if (!this.reconnectTimer) return;
|
|
839
|
+
clearTimeout(this.reconnectTimer);
|
|
840
|
+
this.reconnectTimer = null;
|
|
841
|
+
}
|
|
842
|
+
closeChangeStream() {
|
|
843
|
+
if (!this.changeStream) return;
|
|
844
|
+
this.changeStream.close().catch(() => {});
|
|
845
|
+
this.changeStream = null;
|
|
846
|
+
}
|
|
850
847
|
/**
|
|
851
848
|
* Close the change stream cursor and emit closed event.
|
|
852
849
|
*/
|
|
@@ -855,10 +852,7 @@ var ChangeStreamHandler = class {
|
|
|
855
852
|
clearTimeout(this.debounceTimer);
|
|
856
853
|
this.debounceTimer = null;
|
|
857
854
|
}
|
|
858
|
-
|
|
859
|
-
clearTimeout(this.reconnectTimer);
|
|
860
|
-
this.reconnectTimer = null;
|
|
861
|
-
}
|
|
855
|
+
this.clearReconnectTimer();
|
|
862
856
|
if (this.changeStream) {
|
|
863
857
|
try {
|
|
864
858
|
await this.changeStream.close();
|
|
@@ -876,7 +870,6 @@ var ChangeStreamHandler = class {
|
|
|
876
870
|
return this.usingChangeStreams;
|
|
877
871
|
}
|
|
878
872
|
};
|
|
879
|
-
|
|
880
873
|
//#endregion
|
|
881
874
|
//#region src/scheduler/services/job-manager.ts
|
|
882
875
|
/**
|
|
@@ -1169,7 +1162,6 @@ var JobManager = class {
|
|
|
1169
1162
|
}
|
|
1170
1163
|
}
|
|
1171
1164
|
};
|
|
1172
|
-
|
|
1173
1165
|
//#endregion
|
|
1174
1166
|
//#region src/scheduler/services/job-processor.ts
|
|
1175
1167
|
/**
|
|
@@ -1474,7 +1466,6 @@ var JobProcessor = class {
|
|
|
1474
1466
|
} });
|
|
1475
1467
|
}
|
|
1476
1468
|
};
|
|
1477
|
-
|
|
1478
1469
|
//#endregion
|
|
1479
1470
|
//#region src/scheduler/services/job-query.ts
|
|
1480
1471
|
/**
|
|
@@ -1778,7 +1769,6 @@ var JobQueryService = class JobQueryService {
|
|
|
1778
1769
|
}
|
|
1779
1770
|
}
|
|
1780
1771
|
};
|
|
1781
|
-
|
|
1782
1772
|
//#endregion
|
|
1783
1773
|
//#region src/scheduler/services/job-scheduler.ts
|
|
1784
1774
|
/**
|
|
@@ -2009,7 +1999,6 @@ var JobScheduler = class {
|
|
|
2009
1999
|
}
|
|
2010
2000
|
}
|
|
2011
2001
|
};
|
|
2012
|
-
|
|
2013
2002
|
//#endregion
|
|
2014
2003
|
//#region src/scheduler/services/lifecycle-manager.ts
|
|
2015
2004
|
/**
|
|
@@ -2117,7 +2106,6 @@ var LifecycleManager = class {
|
|
|
2117
2106
|
if (deletions.length > 0) await Promise.all(deletions);
|
|
2118
2107
|
}
|
|
2119
2108
|
};
|
|
2120
|
-
|
|
2121
2109
|
//#endregion
|
|
2122
2110
|
//#region src/scheduler/monque.ts
|
|
2123
2111
|
/**
|
|
@@ -3039,12 +3027,12 @@ var Monque = class extends node_events.EventEmitter {
|
|
|
3039
3027
|
*/
|
|
3040
3028
|
async stop() {
|
|
3041
3029
|
if (!this.isRunning) return;
|
|
3030
|
+
this.lifecycleManager.stopTimers();
|
|
3042
3031
|
this.isRunning = false;
|
|
3043
3032
|
this._query?.clearStatsCache();
|
|
3044
3033
|
try {
|
|
3045
3034
|
await this.changeStreamHandler.close();
|
|
3046
3035
|
} catch {}
|
|
3047
|
-
this.lifecycleManager.stopTimers();
|
|
3048
3036
|
if (this.getActiveJobs().length === 0) return;
|
|
3049
3037
|
let checkInterval;
|
|
3050
3038
|
const waitForJobs = new Promise((resolve) => {
|
|
@@ -3166,7 +3154,6 @@ var Monque = class extends node_events.EventEmitter {
|
|
|
3166
3154
|
return super.off(event, listener);
|
|
3167
3155
|
}
|
|
3168
3156
|
};
|
|
3169
|
-
|
|
3170
3157
|
//#endregion
|
|
3171
3158
|
exports.AggregationTimeoutError = AggregationTimeoutError;
|
|
3172
3159
|
exports.ConnectionError = ConnectionError;
|
|
@@ -3194,4 +3181,5 @@ exports.isProcessingJob = isProcessingJob;
|
|
|
3194
3181
|
exports.isRecurringJob = isRecurringJob;
|
|
3195
3182
|
exports.isValidJobStatus = isValidJobStatus;
|
|
3196
3183
|
exports.validateCronExpression = validateCronExpression;
|
|
3184
|
+
|
|
3197
3185
|
//# sourceMappingURL=index.cjs.map
|