@clipboard-health/mongo-jobs 0.1.0

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.
Files changed (62) hide show
  1. package/README.md +26 -0
  2. package/package.json +41 -0
  3. package/src/index.d.ts +3 -0
  4. package/src/index.js +6 -0
  5. package/src/index.js.map +1 -0
  6. package/src/lib/backgroundJobs.d.ts +59 -0
  7. package/src/lib/backgroundJobs.js +108 -0
  8. package/src/lib/backgroundJobs.js.map +1 -0
  9. package/src/lib/handler.d.ts +6 -0
  10. package/src/lib/handler.js +3 -0
  11. package/src/lib/handler.js.map +1 -0
  12. package/src/lib/internal/cron.d.ts +29 -0
  13. package/src/lib/internal/cron.js +89 -0
  14. package/src/lib/internal/cron.js.map +1 -0
  15. package/src/lib/internal/duplicateRunningError.d.ts +2 -0
  16. package/src/lib/internal/duplicateRunningError.js +7 -0
  17. package/src/lib/internal/duplicateRunningError.js.map +1 -0
  18. package/src/lib/internal/handlerAlreadyRegisteredError.d.ts +2 -0
  19. package/src/lib/internal/handlerAlreadyRegisteredError.js +7 -0
  20. package/src/lib/internal/handlerAlreadyRegisteredError.js.map +1 -0
  21. package/src/lib/internal/jobsRepository.d.ts +51 -0
  22. package/src/lib/internal/jobsRepository.js +170 -0
  23. package/src/lib/internal/jobsRepository.js.map +1 -0
  24. package/src/lib/internal/logger.d.ts +4 -0
  25. package/src/lib/internal/logger.js +3 -0
  26. package/src/lib/internal/logger.js.map +1 -0
  27. package/src/lib/internal/metrics.d.ts +32 -0
  28. package/src/lib/internal/metrics.js +106 -0
  29. package/src/lib/internal/metrics.js.map +1 -0
  30. package/src/lib/internal/mongoDuplicate.d.ts +2 -0
  31. package/src/lib/internal/mongoDuplicate.js +9 -0
  32. package/src/lib/internal/mongoDuplicate.js.map +1 -0
  33. package/src/lib/internal/registry.d.ts +27 -0
  34. package/src/lib/internal/registry.js +78 -0
  35. package/src/lib/internal/registry.js.map +1 -0
  36. package/src/lib/internal/worker/actionableQueues.d.ts +7 -0
  37. package/src/lib/internal/worker/actionableQueues.js +32 -0
  38. package/src/lib/internal/worker/actionableQueues.js.map +1 -0
  39. package/src/lib/internal/worker/fairQueueConsumer.d.ts +24 -0
  40. package/src/lib/internal/worker/fairQueueConsumer.js +127 -0
  41. package/src/lib/internal/worker/fairQueueConsumer.js.map +1 -0
  42. package/src/lib/internal/worker/futureQueues.d.ts +5 -0
  43. package/src/lib/internal/worker/futureQueues.js +27 -0
  44. package/src/lib/internal/worker/futureQueues.js.map +1 -0
  45. package/src/lib/internal/worker/queueConsumer.d.ts +11 -0
  46. package/src/lib/internal/worker/queueConsumer.js +3 -0
  47. package/src/lib/internal/worker/queueConsumer.js.map +1 -0
  48. package/src/lib/internal/worker.d.ts +65 -0
  49. package/src/lib/internal/worker.js +342 -0
  50. package/src/lib/internal/worker.js.map +1 -0
  51. package/src/lib/job.d.ts +37 -0
  52. package/src/lib/job.js +29 -0
  53. package/src/lib/job.js.map +1 -0
  54. package/src/lib/schedule.d.ts +21 -0
  55. package/src/lib/schedule.js +16 -0
  56. package/src/lib/schedule.js.map +1 -0
  57. package/src/lib/testing.d.ts +9 -0
  58. package/src/lib/testing.js +37 -0
  59. package/src/lib/testing.js.map +1 -0
  60. package/src/lib/tracing.d.ts +8 -0
  61. package/src/lib/tracing.js +194 -0
  62. package/src/lib/tracing.js.map +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobsRepository.js","sourceRoot":"","sources":["../../../../../../packages/mongo-jobs/src/lib/internal/jobsRepository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export interface Logger {
2
+ info: (...arguments_: unknown[]) => void;
3
+ error: (...arguments_: unknown[]) => void;
4
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../../../../packages/mongo-jobs/src/lib/internal/logger.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ import { type ClientOptions } from "hot-shots";
2
+ import type mongoose from "mongoose";
3
+ import type { BackgroundJobType } from "../job";
4
+ import type { Registry } from "./registry";
5
+ export interface MetricsReporter {
6
+ gauge(name: string, value: number, tags: Record<string, string>): void;
7
+ increment(name: string, tags: Record<string, string>): void;
8
+ timing(name: string, value: number | Date, tags: Record<string, string>): void;
9
+ }
10
+ /**
11
+ * Create a StatsD reporter.
12
+ */
13
+ export declare function defaultMetricsReporter(options?: ClientOptions): MetricsReporter;
14
+ export declare class Metrics {
15
+ private readonly reporter;
16
+ private readonly jobModel;
17
+ private readonly registry;
18
+ private reportingInterval?;
19
+ private started;
20
+ private reporting;
21
+ constructor(reporter: MetricsReporter, jobModel: mongoose.Model<BackgroundJobType<unknown>>, registry: Registry);
22
+ startReporting(): Promise<void>;
23
+ stopReporting(): void;
24
+ reportMetrics(): Promise<void>;
25
+ increment(queue: string, state: string): void;
26
+ timing(queue: string, metric: string, value: Date | number): void;
27
+ private reportQueueMetric;
28
+ private sendGaugeMetric;
29
+ private scheduledJobsQuery;
30
+ private pendingJobsQuery;
31
+ private failedJobsQuery;
32
+ }
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Metrics = void 0;
4
+ exports.defaultMetricsReporter = defaultMetricsReporter;
5
+ const tslib_1 = require("tslib");
6
+ const hot_shots_1 = tslib_1.__importDefault(require("hot-shots"));
7
+ const tracing_1 = require("../tracing");
8
+ const REPORTING_PERIOD_MILLIS = 60_000;
9
+ /**
10
+ * Create a StatsD reporter.
11
+ */
12
+ function defaultMetricsReporter(options = {}) {
13
+ return new hot_shots_1.default({ ...options, prefix: "background_jobs." });
14
+ }
15
+ class Metrics {
16
+ reporter;
17
+ jobModel;
18
+ registry;
19
+ reportingInterval;
20
+ started = false;
21
+ reporting = false;
22
+ constructor(reporter, jobModel, registry) {
23
+ this.reporter = reporter;
24
+ this.jobModel = jobModel;
25
+ this.registry = registry;
26
+ }
27
+ async startReporting() {
28
+ if (this.started) {
29
+ return;
30
+ }
31
+ this.started = true;
32
+ void this.reportMetrics();
33
+ this.reportingInterval = setInterval(async () => {
34
+ await this.reportMetrics();
35
+ }, REPORTING_PERIOD_MILLIS);
36
+ }
37
+ stopReporting() {
38
+ this.started = false;
39
+ clearInterval(this.reportingInterval);
40
+ }
41
+ async reportMetrics() {
42
+ if (!this.started || this.reporting) {
43
+ return;
44
+ }
45
+ this.reporting = true;
46
+ /* Reporting for each queue separate instead of having 1 query that
47
+ * calculates given state for all queues at once (with groupBy) because
48
+ * during testing it seemed that having queries per each queue was
49
+ * collectively faster than doing one big query (tested with ~1 million
50
+ * jobs in DB)
51
+ */
52
+ try {
53
+ await (0, tracing_1.withInternalsTrace)("reportMetrics", async () => {
54
+ await Promise.all(this.registry.getQueues().map(async (queue) => {
55
+ // Scheduled jobs - jobs who have nextRunAt in the future
56
+ const scheduledCount = await this.reportQueueMetric(queue, "scheduled", this.scheduledJobsQuery(queue));
57
+ // Pending jobs - jobs ready to be processed (nextRunAt is in the past/present)
58
+ const pendingCount = await this.reportQueueMetric(queue, "pending", this.pendingJobsQuery(queue));
59
+ // Failed jobs - those that reached the limit of retries and won't be retried again
60
+ await this.reportQueueMetric(queue, "failed", this.failedJobsQuery(queue));
61
+ // Created jobs - scheduled + pending - reporting to be consistent with pg-boss/postgres background jobs
62
+ this.sendGaugeMetric(queue, "created", scheduledCount + pendingCount);
63
+ }));
64
+ });
65
+ }
66
+ finally {
67
+ this.reporting = false;
68
+ }
69
+ }
70
+ increment(queue, state) {
71
+ this.reporter.increment(`queue.${state}`, { queue });
72
+ }
73
+ timing(queue, metric, value) {
74
+ this.reporter.timing(`queue.${metric}`, value, { queue });
75
+ }
76
+ async reportQueueMetric(queue, state, query) {
77
+ const count = await this.jobModel.countDocuments(query);
78
+ this.sendGaugeMetric(queue, state, count);
79
+ return count;
80
+ }
81
+ sendGaugeMetric(queue, state, value) {
82
+ this.reporter.gauge(`queue.${state}`, value, { queue });
83
+ }
84
+ scheduledJobsQuery(queue) {
85
+ return {
86
+ queue,
87
+ nextRunAt: { $gt: new Date() },
88
+ lockedAt: null,
89
+ };
90
+ }
91
+ pendingJobsQuery(queue) {
92
+ return {
93
+ queue,
94
+ nextRunAt: { $lte: new Date() },
95
+ lockedAt: null,
96
+ };
97
+ }
98
+ failedJobsQuery(queue) {
99
+ return {
100
+ originalQueue: queue,
101
+ failedAt: { $ne: null },
102
+ };
103
+ }
104
+ }
105
+ exports.Metrics = Metrics;
106
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../../../../../packages/mongo-jobs/src/lib/internal/metrics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { MongoServerError } from "mongodb";
2
+ export declare function isMongoDuplicateError(error: unknown): error is MongoServerError;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isMongoDuplicateError = isMongoDuplicateError;
4
+ const mongodb_1 = require("mongodb");
5
+ const MONGO_DUPLICATE_KEY_ERROR_CODE = 11_000;
6
+ function isMongoDuplicateError(error) {
7
+ return error instanceof mongodb_1.MongoServerError && error.code === MONGO_DUPLICATE_KEY_ERROR_CODE;
8
+ }
9
+ //# sourceMappingURL=mongoDuplicate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mongoDuplicate.js","sourceRoot":"","sources":["../../../../../../packages/mongo-jobs/src/lib/internal/mongoDuplicate.ts"],"names":[],"mappings":";;AAIA,sDAEC;AAND,qCAA2C;AAE3C,MAAM,8BAA8B,GAAG,MAAM,CAAC;AAE9C,SAAgB,qBAAqB,CAAC,KAAc;IAClD,OAAO,KAAK,YAAY,0BAAgB,IAAI,KAAK,CAAC,IAAI,KAAK,8BAA8B,CAAC;AAC5F,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { HandlerInterface } from "../handler";
2
+ type InstantiableHandlerClass<T> = new () => HandlerInterface<T>;
3
+ export type AnyHandlerClass<T> = new (...arguments_: any) => HandlerInterface<T>;
4
+ export type InstantiableHandlerClassOrInstance<T> = InstantiableHandlerClass<T> | HandlerInterface<T>;
5
+ export type AnyHandlerClassOrInstance<T> = AnyHandlerClass<T> | HandlerInterface<T>;
6
+ export interface RegisteredHandlerType<T> {
7
+ group: string;
8
+ queue: string;
9
+ handler: HandlerInterface<T>;
10
+ }
11
+ interface ConstructorOptions {
12
+ allowHandlerOverride: boolean | undefined;
13
+ }
14
+ export declare class Registry {
15
+ private readonly handlers;
16
+ private readonly names;
17
+ private readonly queues;
18
+ private readonly queueGroups;
19
+ private readonly allowHandlerOverride;
20
+ constructor(options: ConstructorOptions);
21
+ register<T>(handlerClassOrInstance: InstantiableHandlerClassOrInstance<T>, group: string): void;
22
+ getRegisteredHandler(handler: string | AnyHandlerClassOrInstance<unknown>): RegisteredHandlerType<unknown>;
23
+ getQueues(): string[];
24
+ getQueuesForGroups(groups: string[]): string[];
25
+ private getHandlerName;
26
+ }
27
+ export {};
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Registry = void 0;
4
+ const handlerAlreadyRegisteredError_1 = require("./handlerAlreadyRegisteredError");
5
+ function isHandlerInstance(handler) {
6
+ return handler.perform !== undefined;
7
+ }
8
+ function getHandlerClass(handler) {
9
+ return isHandlerInstance(handler) ? handler.constructor : handler;
10
+ }
11
+ function getHandlerInstance(handler) {
12
+ // eslint-disable-next-line new-cap
13
+ return isHandlerInstance(handler) ? handler : new handler();
14
+ }
15
+ class Registry {
16
+ handlers = new Map();
17
+ names = new Map();
18
+ queues = new Set();
19
+ queueGroups = new Map();
20
+ allowHandlerOverride;
21
+ constructor(options) {
22
+ this.allowHandlerOverride = options.allowHandlerOverride ?? false;
23
+ }
24
+ register(handlerClassOrInstance, group) {
25
+ const handler = getHandlerInstance(handlerClassOrInstance);
26
+ const handlerName = handler.name;
27
+ const queue = handlerName;
28
+ if (!this.allowHandlerOverride && this.handlers.has(handlerName)) {
29
+ throw new handlerAlreadyRegisteredError_1.HandlerAlreadyRegisteredError(`${handlerName} already registered`);
30
+ }
31
+ this.handlers.set(handlerName, { group, handler, queue });
32
+ this.names.set(handler.constructor, handlerName);
33
+ this.queues.add(queue);
34
+ const queueGroup = this.queueGroups.get(group);
35
+ if (queueGroup) {
36
+ queueGroup.add(queue);
37
+ }
38
+ else {
39
+ const newQueueGroup = new Set();
40
+ newQueueGroup.add(queue);
41
+ this.queueGroups.set(group, newQueueGroup);
42
+ }
43
+ }
44
+ getRegisteredHandler(handler) {
45
+ const handlerName = this.getHandlerName(handler);
46
+ const registeredHandler = this.handlers.get(handlerName);
47
+ if (registeredHandler === undefined) {
48
+ throw new Error(`No handler registered for ${handlerName}`);
49
+ }
50
+ return registeredHandler;
51
+ }
52
+ getQueues() {
53
+ return [...this.queues];
54
+ }
55
+ getQueuesForGroups(groups) {
56
+ let result = new Array();
57
+ for (const group of groups) {
58
+ const queuesInGroup = this.queueGroups.get(group);
59
+ if (queuesInGroup) {
60
+ result = [...result, ...queuesInGroup];
61
+ }
62
+ }
63
+ return result;
64
+ }
65
+ getHandlerName(handler) {
66
+ if (typeof handler === "string") {
67
+ return handler;
68
+ }
69
+ const handlerClass = getHandlerClass(handler);
70
+ const name = this.names.get(handlerClass);
71
+ if (name === undefined) {
72
+ throw new Error(`No handler registered for ${handlerClass.toString()}`);
73
+ }
74
+ return name;
75
+ }
76
+ }
77
+ exports.Registry = Registry;
78
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../../../../packages/mongo-jobs/src/lib/internal/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare class ActionableQueues {
2
+ private readonly set;
3
+ private readonly array;
4
+ add(queue: string): void;
5
+ remove(queue: string): void;
6
+ getRandom(): string | undefined;
7
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ActionableQueues = void 0;
4
+ class ActionableQueues {
5
+ set = new Set();
6
+ array = new Array();
7
+ add(queue) {
8
+ if (this.set.has(queue)) {
9
+ return;
10
+ }
11
+ this.set.add(queue);
12
+ this.array.push(queue);
13
+ }
14
+ remove(queue) {
15
+ if (!this.set.has(queue)) {
16
+ return;
17
+ }
18
+ this.set.delete(queue);
19
+ const indexOfQueue = this.array.indexOf(queue);
20
+ this.array.splice(indexOfQueue, 1);
21
+ }
22
+ getRandom() {
23
+ const arrayLength = this.array.length;
24
+ if (arrayLength === 0) {
25
+ return;
26
+ }
27
+ // eslint-disable-next-line consistent-return
28
+ return this.array[Math.floor(Math.random() * arrayLength)];
29
+ }
30
+ }
31
+ exports.ActionableQueues = ActionableQueues;
32
+ //# sourceMappingURL=actionableQueues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actionableQueues.js","sourceRoot":"","sources":["../../../../../../../packages/mongo-jobs/src/lib/internal/worker/actionableQueues.ts"],"names":[],"mappings":";;;AAAA,MAAa,gBAAgB;IACV,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IACxB,KAAK,GAAG,IAAI,KAAK,EAAU,CAAC;IAE7C,GAAG,CAAC,KAAa;QACf,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,MAAM,CAAC,KAAa;QAClB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAEvB,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;IACrC,CAAC;IAED,SAAS;QACP,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;QACtC,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QAED,6CAA6C;QAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC;IAC7D,CAAC;CACF;AAjCD,4CAiCC"}
@@ -0,0 +1,24 @@
1
+ import type { BackgroundJobType } from "../../job";
2
+ import { type JobsRepository } from "../jobsRepository";
3
+ import type { QueueConsumer, QueueConsumerStartOptions } from "./queueConsumer";
4
+ export declare class FairQueueConsumer extends EventTarget implements QueueConsumer {
5
+ private readonly jobsRepository;
6
+ private readonly consumedQueues;
7
+ private readonly consumedQueuesSet;
8
+ private readonly actionableQueues;
9
+ private readonly futureQueues;
10
+ private jobsChangeStream?;
11
+ private refreshQueuesInterval?;
12
+ constructor(queues: string[], jobsRepository: JobsRepository);
13
+ start({ useChangeStream, refreshQueuesIntervalMS }: QueueConsumerStartOptions): Promise<void>;
14
+ startQueuesRefresh(interval: number): Promise<void>;
15
+ startListeningForQueueChanges(): void;
16
+ stop(): Promise<void>;
17
+ refreshActionableQueuesFromDB(): Promise<void>;
18
+ acquireNextJob(): Promise<BackgroundJobType<unknown> | undefined>;
19
+ getConsumedQueues(): string[];
20
+ private promoteQueues;
21
+ private removeQueueFromActionable;
22
+ private refreshQueueFutureActionableAt;
23
+ private dispatchNewJobEvent;
24
+ }
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FairQueueConsumer = void 0;
4
+ const actionableQueues_1 = require("./actionableQueues");
5
+ const futureQueues_1 = require("./futureQueues");
6
+ class FairQueueConsumer extends EventTarget {
7
+ jobsRepository;
8
+ consumedQueues;
9
+ consumedQueuesSet;
10
+ actionableQueues = new actionableQueues_1.ActionableQueues();
11
+ futureQueues = new futureQueues_1.FutureQueues();
12
+ jobsChangeStream;
13
+ refreshQueuesInterval;
14
+ constructor(queues, jobsRepository) {
15
+ super();
16
+ this.consumedQueues = queues;
17
+ this.consumedQueuesSet = new Set(queues);
18
+ this.jobsRepository = jobsRepository;
19
+ }
20
+ async start({ useChangeStream, refreshQueuesIntervalMS }) {
21
+ if (this.consumedQueues.length === 0) {
22
+ return;
23
+ }
24
+ await this.startQueuesRefresh(refreshQueuesIntervalMS);
25
+ if (useChangeStream && !this.jobsChangeStream) {
26
+ this.startListeningForQueueChanges();
27
+ }
28
+ }
29
+ async startQueuesRefresh(interval) {
30
+ await this.refreshActionableQueuesFromDB();
31
+ this.refreshQueuesInterval = setInterval(async () => {
32
+ await this.refreshActionableQueuesFromDB();
33
+ }, interval);
34
+ }
35
+ startListeningForQueueChanges() {
36
+ this.jobsChangeStream = this.jobsRepository.watchUpserts(this.consumedQueues);
37
+ this.jobsChangeStream.on("change", (event) => {
38
+ const typedEvent = event;
39
+ const queue = typedEvent.fullDocument?.queue;
40
+ const nextRunAt = typedEvent.fullDocument?.nextRunAt;
41
+ const lockedAt = typedEvent.fullDocument?.lockedAt;
42
+ if (queue === undefined ||
43
+ nextRunAt === undefined ||
44
+ !this.consumedQueuesSet.has(queue) ||
45
+ lockedAt !== undefined) {
46
+ return;
47
+ }
48
+ this.futureQueues.setActionableAt(queue, nextRunAt);
49
+ this.dispatchNewJobEvent();
50
+ });
51
+ }
52
+ async stop() {
53
+ if (this.refreshQueuesInterval) {
54
+ clearInterval(this.refreshQueuesInterval);
55
+ }
56
+ if (this.jobsChangeStream) {
57
+ this.jobsChangeStream.removeAllListeners();
58
+ try {
59
+ await this.jobsChangeStream.close();
60
+ }
61
+ catch {
62
+ // Might already be closed
63
+ }
64
+ }
65
+ }
66
+ async refreshActionableQueuesFromDB() {
67
+ const queues = await this.jobsRepository.fetchQueuesWithJobs(this.consumedQueues);
68
+ for (const queue of queues) {
69
+ /**
70
+ * Not adding "undefined" into actionable queues - those are jobs that were failed and were
71
+ * taken off of their respective queues
72
+ **/
73
+ if (queue) {
74
+ this.actionableQueues.add(queue);
75
+ }
76
+ }
77
+ }
78
+ async acquireNextJob() {
79
+ this.promoteQueues();
80
+ let job;
81
+ while (!job) {
82
+ const queue = this.actionableQueues.getRandom();
83
+ if (queue === undefined) {
84
+ return undefined;
85
+ }
86
+ // eslint-disable-next-line no-await-in-loop
87
+ job = await this.jobsRepository.fetchAndLockNextJob([queue]);
88
+ if (!job) {
89
+ // eslint-disable-next-line no-await-in-loop
90
+ await this.removeQueueFromActionable(queue);
91
+ }
92
+ }
93
+ return job;
94
+ }
95
+ getConsumedQueues() {
96
+ return [...this.consumedQueues];
97
+ }
98
+ promoteQueues() {
99
+ const queuesToPromote = this.futureQueues.acquireCurrentlyActionable();
100
+ for (const queue of queuesToPromote) {
101
+ this.actionableQueues.add(queue);
102
+ }
103
+ }
104
+ async removeQueueFromActionable(queue) {
105
+ this.actionableQueues.remove(queue);
106
+ void this.refreshQueueFutureActionableAt(queue);
107
+ }
108
+ async refreshQueueFutureActionableAt(queue) {
109
+ const job = await this.jobsRepository.fetchNextJob(queue);
110
+ if (!job) {
111
+ return;
112
+ }
113
+ const { nextRunAt } = job;
114
+ if (!nextRunAt) {
115
+ return;
116
+ }
117
+ this.futureQueues.setActionableAt(queue, nextRunAt);
118
+ if (nextRunAt < new Date()) {
119
+ this.dispatchNewJobEvent();
120
+ }
121
+ }
122
+ dispatchNewJobEvent() {
123
+ this.dispatchEvent(new Event("newJob"));
124
+ }
125
+ }
126
+ exports.FairQueueConsumer = FairQueueConsumer;
127
+ //# sourceMappingURL=fairQueueConsumer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fairQueueConsumer.js","sourceRoot":"","sources":["../../../../../../../packages/mongo-jobs/src/lib/internal/worker/fairQueueConsumer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare class FutureQueues {
2
+ private readonly actionableSoon;
3
+ setActionableAt(queue: string, actionableAt: Date): void;
4
+ acquireCurrentlyActionable(): string[];
5
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FutureQueues = void 0;
4
+ class FutureQueues {
5
+ actionableSoon = new Map();
6
+ setActionableAt(queue, actionableAt) {
7
+ const oldActionableAt = this.actionableSoon.get(queue);
8
+ if (oldActionableAt && oldActionableAt < actionableAt) {
9
+ return;
10
+ }
11
+ this.actionableSoon.set(queue, actionableAt);
12
+ }
13
+ acquireCurrentlyActionable() {
14
+ const actionableQueues = new Array();
15
+ const now = new Date();
16
+ this.actionableSoon.forEach((actionableAt, queue) => {
17
+ if (actionableAt > now) {
18
+ return;
19
+ }
20
+ actionableQueues.push(queue);
21
+ this.actionableSoon.delete(queue);
22
+ });
23
+ return actionableQueues;
24
+ }
25
+ }
26
+ exports.FutureQueues = FutureQueues;
27
+ //# sourceMappingURL=futureQueues.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"futureQueues.js","sourceRoot":"","sources":["../../../../../../../packages/mongo-jobs/src/lib/internal/worker/futureQueues.ts"],"names":[],"mappings":";;;AAAA,MAAa,YAAY;IACN,cAAc,GAAG,IAAI,GAAG,EAAgB,CAAC;IAE1D,eAAe,CAAC,KAAa,EAAE,YAAkB;QAC/C,MAAM,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAEvD,IAAI,eAAe,IAAI,eAAe,GAAG,YAAY,EAAE,CAAC;YACtD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IAC/C,CAAC;IAED,0BAA0B;QACxB,MAAM,gBAAgB,GAAG,IAAI,KAAK,EAAU,CAAC;QAC7C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,KAAK,EAAE,EAAE;YAClD,IAAI,YAAY,GAAG,GAAG,EAAE,CAAC;gBACvB,OAAO;YACT,CAAC;YAED,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC7B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,OAAO,gBAAgB,CAAC;IAC1B,CAAC;CACF;AA5BD,oCA4BC"}
@@ -0,0 +1,11 @@
1
+ import type { BackgroundJobType } from "../../job";
2
+ export interface QueueConsumerStartOptions {
3
+ useChangeStream: boolean;
4
+ refreshQueuesIntervalMS: number;
5
+ }
6
+ export interface QueueConsumer extends EventTarget {
7
+ acquireNextJob(): Promise<BackgroundJobType<unknown> | undefined>;
8
+ start(options: QueueConsumerStartOptions): Promise<void>;
9
+ stop(): Promise<void>;
10
+ getConsumedQueues(): string[];
11
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=queueConsumer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queueConsumer.js","sourceRoot":"","sources":["../../../../../../../packages/mongo-jobs/src/lib/internal/worker/queueConsumer.ts"],"names":[],"mappings":""}
@@ -0,0 +1,65 @@
1
+ import type { BackgroundJobType } from "../job";
2
+ import type { Cron } from "./cron";
3
+ import type { JobsRepository } from "./jobsRepository";
4
+ import type { Logger } from "./logger";
5
+ import type { Metrics } from "./metrics";
6
+ import type { Registry } from "./registry";
7
+ export interface WorkerOptions {
8
+ maxConcurrency?: number;
9
+ newJobCheckWaitMS?: number;
10
+ useChangeStream?: boolean;
11
+ lockTimeoutMS?: number;
12
+ unlockJobsIntervalMS?: number;
13
+ refreshQueuesIntervalMS?: number;
14
+ exclude?: string[];
15
+ }
16
+ interface ConstructorOptions extends WorkerOptions {
17
+ groups: string[];
18
+ metrics: Metrics;
19
+ logger: Logger | undefined;
20
+ jobsRepo: JobsRepository;
21
+ registry: Registry;
22
+ cron: Cron;
23
+ }
24
+ export declare class Worker {
25
+ stopped: boolean;
26
+ private readonly lockTimeoutMS;
27
+ private readonly maxConcurrency;
28
+ private readonly newJobCheckWaitMS;
29
+ private readonly useChangeStream;
30
+ private readonly unlockJobsIntervalMS;
31
+ private readonly refreshQueuesIntervalMS;
32
+ private readonly jobsRepo;
33
+ private readonly runningJobs;
34
+ private readonly metrics;
35
+ private readonly registry;
36
+ private readonly cron;
37
+ private readonly logger;
38
+ private readonly queueConsumer;
39
+ private unlockJobsInterval?;
40
+ private workerLoopInterval?;
41
+ private fetchingJobs;
42
+ private newJobsWatched;
43
+ private nextAvailableJobCheck;
44
+ constructor(options: ConstructorOptions);
45
+ start(): Promise<void>;
46
+ acquireNextJob(): Promise<BackgroundJobType<unknown> | undefined>;
47
+ runJobHandler(job: BackgroundJobType<unknown>): Promise<void>;
48
+ stop(waitTime?: number): Promise<void>;
49
+ markJobCompleted(job: BackgroundJobType<unknown>): Promise<void>;
50
+ getConsumedQueues(): string[];
51
+ updateUniqueKey(job: BackgroundJobType<unknown>): Promise<void>;
52
+ private workerLoop;
53
+ private handleNewJobEvent;
54
+ private scheduleWorkerLoopAfterWait;
55
+ private performJob;
56
+ private handleJobError;
57
+ private duplicateReschedule;
58
+ private unlockStuckJobs;
59
+ private reportExecutionDelay;
60
+ private logContext;
61
+ private getMaxAttemptsForJob;
62
+ private markJobFailed;
63
+ private scheduleRetry;
64
+ }
65
+ export {};