@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.
- package/README.md +26 -0
- package/package.json +41 -0
- package/src/index.d.ts +3 -0
- package/src/index.js +6 -0
- package/src/index.js.map +1 -0
- package/src/lib/backgroundJobs.d.ts +59 -0
- package/src/lib/backgroundJobs.js +108 -0
- package/src/lib/backgroundJobs.js.map +1 -0
- package/src/lib/handler.d.ts +6 -0
- package/src/lib/handler.js +3 -0
- package/src/lib/handler.js.map +1 -0
- package/src/lib/internal/cron.d.ts +29 -0
- package/src/lib/internal/cron.js +89 -0
- package/src/lib/internal/cron.js.map +1 -0
- package/src/lib/internal/duplicateRunningError.d.ts +2 -0
- package/src/lib/internal/duplicateRunningError.js +7 -0
- package/src/lib/internal/duplicateRunningError.js.map +1 -0
- package/src/lib/internal/handlerAlreadyRegisteredError.d.ts +2 -0
- package/src/lib/internal/handlerAlreadyRegisteredError.js +7 -0
- package/src/lib/internal/handlerAlreadyRegisteredError.js.map +1 -0
- package/src/lib/internal/jobsRepository.d.ts +51 -0
- package/src/lib/internal/jobsRepository.js +170 -0
- package/src/lib/internal/jobsRepository.js.map +1 -0
- package/src/lib/internal/logger.d.ts +4 -0
- package/src/lib/internal/logger.js +3 -0
- package/src/lib/internal/logger.js.map +1 -0
- package/src/lib/internal/metrics.d.ts +32 -0
- package/src/lib/internal/metrics.js +106 -0
- package/src/lib/internal/metrics.js.map +1 -0
- package/src/lib/internal/mongoDuplicate.d.ts +2 -0
- package/src/lib/internal/mongoDuplicate.js +9 -0
- package/src/lib/internal/mongoDuplicate.js.map +1 -0
- package/src/lib/internal/registry.d.ts +27 -0
- package/src/lib/internal/registry.js +78 -0
- package/src/lib/internal/registry.js.map +1 -0
- package/src/lib/internal/worker/actionableQueues.d.ts +7 -0
- package/src/lib/internal/worker/actionableQueues.js +32 -0
- package/src/lib/internal/worker/actionableQueues.js.map +1 -0
- package/src/lib/internal/worker/fairQueueConsumer.d.ts +24 -0
- package/src/lib/internal/worker/fairQueueConsumer.js +127 -0
- package/src/lib/internal/worker/fairQueueConsumer.js.map +1 -0
- package/src/lib/internal/worker/futureQueues.d.ts +5 -0
- package/src/lib/internal/worker/futureQueues.js +27 -0
- package/src/lib/internal/worker/futureQueues.js.map +1 -0
- package/src/lib/internal/worker/queueConsumer.d.ts +11 -0
- package/src/lib/internal/worker/queueConsumer.js +3 -0
- package/src/lib/internal/worker/queueConsumer.js.map +1 -0
- package/src/lib/internal/worker.d.ts +65 -0
- package/src/lib/internal/worker.js +342 -0
- package/src/lib/internal/worker.js.map +1 -0
- package/src/lib/job.d.ts +37 -0
- package/src/lib/job.js +29 -0
- package/src/lib/job.js.map +1 -0
- package/src/lib/schedule.d.ts +21 -0
- package/src/lib/schedule.js +16 -0
- package/src/lib/schedule.js.map +1 -0
- package/src/lib/testing.d.ts +9 -0
- package/src/lib/testing.js +37 -0
- package/src/lib/testing.js.map +1 -0
- package/src/lib/tracing.d.ts +8 -0
- package/src/lib/tracing.js +194 -0
- 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 @@
|
|
|
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,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,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,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 @@
|
|
|
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 {};
|