agenda 4.4.0 → 5.0.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 (80) hide show
  1. package/README.md +20 -8
  2. package/dist/agenda/create.d.ts +2 -2
  3. package/dist/agenda/create.d.ts.map +1 -1
  4. package/dist/agenda/create.js.map +1 -1
  5. package/dist/agenda/define.d.ts +2 -2
  6. package/dist/agenda/define.d.ts.map +1 -1
  7. package/dist/agenda/define.js.map +1 -1
  8. package/dist/agenda/every.d.ts +2 -1
  9. package/dist/agenda/every.d.ts.map +1 -1
  10. package/dist/agenda/every.js +1 -1
  11. package/dist/agenda/every.js.map +1 -1
  12. package/dist/agenda/find-and-lock-next-job.d.ts.map +1 -1
  13. package/dist/agenda/find-and-lock-next-job.js +3 -4
  14. package/dist/agenda/find-and-lock-next-job.js.map +1 -1
  15. package/dist/agenda/now.d.ts +2 -2
  16. package/dist/agenda/now.d.ts.map +1 -1
  17. package/dist/agenda/now.js.map +1 -1
  18. package/dist/agenda/schedule.d.ts +3 -3
  19. package/dist/agenda/schedule.d.ts.map +1 -1
  20. package/dist/agenda/schedule.js.map +1 -1
  21. package/dist/job/compute-next-run-at.js +2 -2
  22. package/dist/job/compute-next-run-at.js.map +1 -1
  23. package/dist/job/index.d.ts +6 -3
  24. package/dist/job/index.d.ts.map +1 -1
  25. package/dist/job/index.js.map +1 -1
  26. package/lib/agenda/cancel.ts +28 -0
  27. package/lib/agenda/close.ts +37 -0
  28. package/lib/agenda/create.ts +22 -0
  29. package/lib/agenda/database.ts +63 -0
  30. package/lib/agenda/db-init.ts +45 -0
  31. package/lib/agenda/default-concurrency.ts +19 -0
  32. package/lib/agenda/default-lock-lifetime.ts +17 -0
  33. package/lib/agenda/default-lock-limit.ts +17 -0
  34. package/lib/agenda/define.ts +85 -0
  35. package/lib/agenda/disable.ts +28 -0
  36. package/lib/agenda/drain.ts +30 -0
  37. package/lib/agenda/enable.ts +29 -0
  38. package/lib/agenda/every.ts +86 -0
  39. package/lib/agenda/find-and-lock-next-job.ts +78 -0
  40. package/lib/agenda/has-mongo-protocol.ts +8 -0
  41. package/lib/agenda/index.ts +227 -0
  42. package/lib/agenda/job-processing-queue.ts +99 -0
  43. package/lib/agenda/jobs.ts +31 -0
  44. package/lib/agenda/lock-limit.ts +17 -0
  45. package/lib/agenda/max-concurrency.ts +20 -0
  46. package/lib/agenda/mongo.ts +21 -0
  47. package/lib/agenda/name.ts +16 -0
  48. package/lib/agenda/now.ts +31 -0
  49. package/lib/agenda/process-every.ts +18 -0
  50. package/lib/agenda/purge.ts +19 -0
  51. package/lib/agenda/save-job.ts +185 -0
  52. package/lib/agenda/schedule.ts +79 -0
  53. package/lib/agenda/sort.ts +17 -0
  54. package/lib/agenda/start.ts +30 -0
  55. package/lib/agenda/stop.ts +49 -0
  56. package/lib/cjs.ts +5 -0
  57. package/lib/index.ts +11 -0
  58. package/lib/job/compute-next-run-at.ts +191 -0
  59. package/lib/job/disable.ts +11 -0
  60. package/lib/job/enable.ts +11 -0
  61. package/lib/job/fail.ts +29 -0
  62. package/lib/job/index.ts +221 -0
  63. package/lib/job/is-running.ts +29 -0
  64. package/lib/job/priority.ts +11 -0
  65. package/lib/job/remove.ts +10 -0
  66. package/lib/job/repeat-at.ts +12 -0
  67. package/lib/job/repeat-every.ts +40 -0
  68. package/lib/job/run.ts +125 -0
  69. package/lib/job/save.ts +11 -0
  70. package/lib/job/schedule.ts +15 -0
  71. package/lib/job/set-shouldsaveresult.ts +10 -0
  72. package/lib/job/to-json.ts +36 -0
  73. package/lib/job/touch.ts +11 -0
  74. package/lib/job/unique.ts +18 -0
  75. package/lib/utils/create-job.ts +13 -0
  76. package/lib/utils/index.ts +3 -0
  77. package/lib/utils/parse-priority.ts +37 -0
  78. package/lib/utils/process-jobs.ts +379 -0
  79. package/package.json +13 -11
  80. package/tsconfig.json +72 -0
@@ -0,0 +1,85 @@
1
+ import { Agenda } from ".";
2
+ import createDebugger from "debug";
3
+ import { Job } from "../job";
4
+
5
+ const debug = createDebugger("agenda:define");
6
+
7
+ export enum JobPriority {
8
+ highest = 20,
9
+ high = 10,
10
+ normal = 0,
11
+ low = -10,
12
+ lowest = -20,
13
+ }
14
+
15
+ export interface DefineOptions {
16
+ /**
17
+ * Maximum number of that job that can be running at once (per instance of agenda)
18
+ */
19
+ concurrency?: number;
20
+
21
+ /**
22
+ * Maximum number of that job that can be locked at once (per instance of agenda)
23
+ */
24
+ lockLimit?: number;
25
+
26
+ /**
27
+ * Interval in ms of how long the job stays locked for (see multiple job processors for more info). A job will
28
+ * automatically unlock if done() is called.
29
+ */
30
+ lockLifetime?: number;
31
+
32
+ /**
33
+ * (lowest|low|normal|high|highest|number) specifies the priority of the job. Higher priority jobs will run
34
+ * first.
35
+ */
36
+ priority?: JobPriority;
37
+
38
+ /**
39
+ * Should the return value of the job be persisted
40
+ */
41
+ shouldSaveResult?: boolean;
42
+ }
43
+
44
+ export type Processor<T> =
45
+ | ((job: Job<T>) => Promise<void>)
46
+ | ((job: Job<T>, done: () => void) => void);
47
+
48
+ /**
49
+ * Setup definition for job
50
+ * Method is used by consumers of lib to setup their functions
51
+ * @name Agenda#define
52
+ * @function
53
+ * @param name name of job
54
+ * @param options options for job to run
55
+ * @param [processor] function to be called to run actual job
56
+ */
57
+ export const define = function<T> (
58
+ this: Agenda,
59
+ name: string,
60
+ options: DefineOptions | Processor<T>,
61
+ processor?: Processor<T>
62
+ ): void {
63
+ if (processor === undefined) {
64
+ processor = options as Processor<T>;
65
+ options = {};
66
+ }
67
+
68
+ this._definitions[name] = {
69
+ fn: processor,
70
+ concurrency:
71
+ (options as DefineOptions).concurrency || this._defaultConcurrency, // `null` is per interface definition of DefineOptions not valid
72
+ lockLimit: (options as DefineOptions).lockLimit || this._defaultLockLimit,
73
+ priority: (options as DefineOptions).priority || JobPriority.normal,
74
+ lockLifetime:
75
+ (options as DefineOptions).lockLifetime || this._defaultLockLifetime,
76
+ running: 0,
77
+ locked: 0,
78
+ shouldSaveResult: (options as DefineOptions).shouldSaveResult || false
79
+ };
80
+ debug(
81
+ "job [%s] defined with following options: \n%O",
82
+ name,
83
+ this._definitions[name]
84
+ );
85
+ };
@@ -0,0 +1,28 @@
1
+ import createDebugger from "debug";
2
+ import { Filter } from "mongodb";
3
+ import { Agenda } from ".";
4
+ const debug = createDebugger("agenda:disable");
5
+
6
+ /**
7
+ * Disables any jobs matching the passed MongoDB query by setting the `disabled` flag to `true`
8
+ * @name Agenda#disable
9
+ * @function
10
+ * @param query MongoDB query to use when enabling
11
+ * @returns {Promise<number>} Resolved with the number of disabled job instances.
12
+ */
13
+ export const disable = async function (
14
+ this: Agenda,
15
+ query: Filter<unknown> = {}
16
+ ): Promise<number> {
17
+ debug("attempting to disable all jobs matching query", query);
18
+ try {
19
+ const { modifiedCount } = await this._collection.updateMany(query, {
20
+ $set: { disabled: true },
21
+ });
22
+ debug("%s jobs disabled");
23
+ return modifiedCount;
24
+ } catch (error) {
25
+ debug("error trying to mark jobs as `disabled`");
26
+ throw error;
27
+ }
28
+ };
@@ -0,0 +1,30 @@
1
+ import createDebugger from "debug";
2
+ import { Agenda } from ".";
3
+
4
+ const debug = createDebugger("agenda:drain");
5
+
6
+ /**
7
+ * Clear the interval that processes the jobs
8
+ * @name Agenda#drain
9
+ * @function
10
+ * @returns resolves when all running jobs completes
11
+ */
12
+ export const drain = async function (this: Agenda): Promise<void> {
13
+ return new Promise((resolve) => {
14
+ debug("Agenda.drain called, clearing interval for processJobs()");
15
+ clearInterval(this._processInterval);
16
+ this._processInterval = undefined;
17
+
18
+ if (this._runningJobs.length === 0) {
19
+ resolve();
20
+ } else {
21
+ debug("Agenda.drain waiting for jobs to finish");
22
+ this.on('complete', () => {
23
+ // running jobs are removed after the event
24
+ if (this._runningJobs.length === 1) {
25
+ resolve();
26
+ }
27
+ });
28
+ }
29
+ });
30
+ };
@@ -0,0 +1,29 @@
1
+ import createDebugger from "debug";
2
+ import { Filter } from "mongodb";
3
+ import { Agenda } from ".";
4
+ const debug = createDebugger("agenda:enable");
5
+
6
+ /**
7
+ * Enables any jobs matching the passed MongoDB query by setting the `disabled` flag to `false`
8
+ * @name Agenda#enable
9
+ * @function
10
+ * @param query MongoDB query to use when enabling
11
+ * @caller client code, Agenda.purge(), Job.remove()
12
+ * @returns {Promise<Number>} A promise that contains the number of removed documents when fulfilled.
13
+ */
14
+ export const enable = async function (
15
+ this: Agenda,
16
+ query: Filter<unknown> = {}
17
+ ): Promise<number> {
18
+ debug("attempting to enable all jobs matching query", query);
19
+ try {
20
+ const { modifiedCount } = await this._collection.updateMany(query, {
21
+ $set: { disabled: false },
22
+ });
23
+ debug("%s jobs enabled", modifiedCount);
24
+ return modifiedCount;
25
+ } catch (error) {
26
+ debug("error trying to mark jobs as `enabled`");
27
+ throw error;
28
+ }
29
+ };
@@ -0,0 +1,86 @@
1
+ import createDebugger from "debug";
2
+ import { Agenda } from ".";
3
+ import { Job, JobAttributesData } from "../job";
4
+ import { JobOptions } from "../job/repeat-every";
5
+
6
+ const debug = createDebugger("agenda:every");
7
+
8
+ /**
9
+ * Creates a scheduled job with given interval and name/names of the job to run
10
+ * @name Agenda#every
11
+ * @function
12
+ * @param interval - run every X interval
13
+ * @param names - String or strings of jobs to schedule
14
+ * @param data - data to run for job
15
+ * @param options - options to run job for
16
+ * @returns Job/s created. Resolves when schedule fails or passes
17
+ */
18
+ export const every = async function<T extends JobAttributesData> (
19
+ this: Agenda,
20
+ interval: string,
21
+ names: string | string[],
22
+ data?: T,
23
+ options?: JobOptions
24
+ ): Promise<any> {
25
+ /**
26
+ * Internal method to setup job that gets run every interval
27
+ * @param interval run every X interval
28
+ * @param name String job to schedule
29
+ * @param [data] data to run for job
30
+ * @param [options] options to run job for
31
+ * @returns instance of job
32
+ */
33
+ const createJob = async<T extends JobAttributesData> (
34
+ interval: string,
35
+ name: string,
36
+ data?: T,
37
+ options?: JobOptions
38
+ ): Promise<Job> => {
39
+ const job = this.create(name, data || {});
40
+
41
+ job.attrs.type = "single";
42
+ job.repeatEvery(interval, options);
43
+ return job.save();
44
+ };
45
+
46
+ /**
47
+ * Internal helper method that uses createJob to create jobs for an array of names
48
+ * @param interval run every X interval
49
+ * @param names Strings of jobs to schedule
50
+ * @param [data] data to run for job
51
+ * @param [options] options to run job for
52
+ * @return array of jobs created
53
+ */
54
+ const createJobs = async (
55
+ interval: string,
56
+ names: string[],
57
+ data?: T,
58
+ options?: JobOptions
59
+ ): Promise<Job[] | undefined> => {
60
+ try {
61
+ const jobs: Array<Promise<Job>> = [];
62
+ names.map((name) => jobs.push(createJob(interval, name, data, options)));
63
+
64
+ debug("every() -> all jobs created successfully");
65
+
66
+ return Promise.all(jobs);
67
+ } catch (error) {
68
+ // @TODO: catch - ignore :O
69
+ debug("every() -> error creating one or more of the jobs", error);
70
+ }
71
+ };
72
+
73
+ if (typeof names === "string") {
74
+ debug("Agenda.every(%s, %O, %O)", interval, names, options);
75
+ const jobs = await createJob(interval, names, data, options);
76
+
77
+ return jobs;
78
+ }
79
+
80
+ if (Array.isArray(names)) {
81
+ debug("Agenda.every(%s, %s, %O)", interval, names, options);
82
+ const jobs = await createJobs(interval, names, data, options);
83
+
84
+ return jobs;
85
+ }
86
+ };
@@ -0,0 +1,78 @@
1
+ import createDebugger from "debug";
2
+ import { createJob } from "../utils";
3
+ import { Agenda } from ".";
4
+ import { Job } from "../job";
5
+ import { ReturnDocument } from "mongodb";
6
+
7
+ const debug = createDebugger("agenda:internal:_findAndLockNextJob");
8
+
9
+ /**
10
+ * Find and lock jobs
11
+ * @name Agenda#findAndLockNextJob
12
+ * @function
13
+ * @param jobName name of job to try to lock
14
+ * @param definition definition used to tell how job is run
15
+ * @access protected
16
+ * @caller jobQueueFilling() only
17
+ */
18
+ export const findAndLockNextJob = async function (
19
+ this: Agenda,
20
+ jobName: string,
21
+ definition: any
22
+ ): Promise<Job | undefined> {
23
+ const now = new Date();
24
+ const lockDeadline = new Date(Date.now().valueOf() - definition.lockLifetime);
25
+ debug("_findAndLockNextJob(%s, [Function])", jobName);
26
+
27
+ const JOB_PROCESS_WHERE_QUERY = {
28
+ $and: [
29
+ {
30
+ name: jobName,
31
+ disabled: { $ne: true },
32
+ },
33
+ {
34
+ $or: [
35
+ {
36
+ lockedAt: { $eq: null },
37
+ nextRunAt: { $lte: this._nextScanAt },
38
+ },
39
+ {
40
+ lockedAt: { $lte: lockDeadline },
41
+ },
42
+ ],
43
+ },
44
+ ],
45
+ };
46
+
47
+ /**
48
+ * Query used to set a job as locked
49
+ * @type {{$set: {lockedAt: Date}}}
50
+ */
51
+ const JOB_PROCESS_SET_QUERY = { $set: { lockedAt: now } };
52
+
53
+ /**
54
+ * Query used to affect what gets returned
55
+ * @type {{returnOriginal: boolean, sort: object}}
56
+ */
57
+ const JOB_RETURN_QUERY = { returnDocument: ReturnDocument.AFTER, sort: this._sort };
58
+
59
+ // Find ONE and ONLY ONE job and set the 'lockedAt' time so that job begins to be processed
60
+ const result = await this._collection.findOneAndUpdate(
61
+ JOB_PROCESS_WHERE_QUERY,
62
+ JOB_PROCESS_SET_QUERY,
63
+ JOB_RETURN_QUERY
64
+ );
65
+
66
+ let job: Job | undefined = undefined;
67
+ if (result.value) {
68
+ debug(
69
+ "found a job available to lock, creating a new job on Agenda with id [%s]",
70
+ result.value._id
71
+ );
72
+
73
+ // @ts-ignore
74
+ job = createJob(this, result.value);
75
+ }
76
+
77
+ return job;
78
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Given a mongo connection url will check if it contains the mongo
3
+ * @param url URL to be tested
4
+ * @returns whether or not the url is a valid mongo URL
5
+ */
6
+ export const hasMongoProtocol = function (url: string): boolean {
7
+ return /mongodb(?:\+srv)?:\/\/.*/.exec(url) !== null;
8
+ };
@@ -0,0 +1,227 @@
1
+ import { EventEmitter } from "events";
2
+ import humanInterval from "human-interval";
3
+ import {
4
+ AnyError,
5
+ Collection,
6
+ Db as MongoDb,
7
+ MongoClient,
8
+ MongoClientOptions,
9
+ } from "mongodb";
10
+ import { Job } from "../job";
11
+ import { cancel } from "./cancel";
12
+ import { close } from "./close";
13
+ import { create } from "./create";
14
+ import { database } from "./database";
15
+ import { dbInit } from "./db-init";
16
+ import { defaultConcurrency } from "./default-concurrency";
17
+ import { defaultLockLifetime } from "./default-lock-lifetime";
18
+ import { defaultLockLimit } from "./default-lock-limit";
19
+ import { define } from "./define";
20
+ import { disable } from "./disable";
21
+ import { enable } from "./enable";
22
+ import { every } from "./every";
23
+ import { findAndLockNextJob } from "./find-and-lock-next-job";
24
+ import { JobProcessingQueue } from "./job-processing-queue";
25
+ import { jobs } from "./jobs";
26
+ import { lockLimit } from "./lock-limit";
27
+ import { maxConcurrency } from "./max-concurrency";
28
+ import { mongo } from "./mongo";
29
+ import { name } from "./name";
30
+ import { now } from "./now";
31
+ import { processEvery } from "./process-every";
32
+ import { purge } from "./purge";
33
+ import { saveJob } from "./save-job";
34
+ import { schedule } from "./schedule";
35
+ import { sort } from "./sort";
36
+ import { start } from "./start";
37
+ import { stop } from "./stop";
38
+ import { drain } from './drain';
39
+
40
+ export interface AgendaConfig {
41
+ name?: string;
42
+ processEvery?: string;
43
+ maxConcurrency?: number;
44
+ defaultConcurrency?: number;
45
+ lockLimit?: number;
46
+ defaultLockLimit?: number;
47
+ defaultLockLifetime?: number;
48
+ sort?: any;
49
+ mongo?: MongoDb;
50
+ db?: {
51
+ address: string;
52
+ collection?: string;
53
+ options?: MongoClientOptions;
54
+ };
55
+ disableAutoIndex?: boolean;
56
+ }
57
+
58
+ /**
59
+ * @class Agenda
60
+ * @param {Object} config - Agenda Config
61
+ * @param {Function} cb - Callback after Agenda has started and connected to mongo
62
+ * @property {Object} _name - Name of the current Agenda queue
63
+ * @property {Number} _processEvery
64
+ * @property {Number} _defaultConcurrency
65
+ * @property {Number} _maxConcurrency
66
+ * @property {Number} _defaultLockLimit
67
+ * @property {Number} _lockLimit
68
+ * @property {Object} _definitions
69
+ * @property {Object} _runningJobs
70
+ * @property {Object} _lockedJobs
71
+ * @property {Object} _jobQueue
72
+ * @property {Number} _defaultLockLifetime
73
+ * @property {Object} _sort
74
+ * @property {Object} _indices
75
+ * @property {Boolean} _isLockingOnTheFly - true if 'lockingOnTheFly' is currently running. Prevent concurrent execution of this method.
76
+ * @property {Map} _isJobQueueFilling - A map of jobQueues and if the 'jobQueueFilling' method is currently running for a given map. 'lockingOnTheFly' and 'jobQueueFilling' should not run concurrently for the same jobQueue. It can cause that lock limits aren't honored.
77
+ * @property {Array} _jobsToLock
78
+ */
79
+ class Agenda extends EventEmitter {
80
+ _defaultConcurrency: any;
81
+ _defaultLockLifetime: any;
82
+ _defaultLockLimit: any;
83
+ _definitions: any;
84
+ _findAndLockNextJob = findAndLockNextJob;
85
+ _indices: any;
86
+ _disableAutoIndex: boolean;
87
+ _isLockingOnTheFly: boolean;
88
+ _isJobQueueFilling: Map<string, boolean>;
89
+ _jobQueue: JobProcessingQueue;
90
+ _jobsToLock: Job[];
91
+ _lockedJobs: Job[];
92
+ _runningJobs: Job[];
93
+ _lockLimit: any;
94
+ _maxConcurrency: any;
95
+ _mongoUseUnifiedTopology?: boolean;
96
+ _name: any;
97
+ _processEvery: number;
98
+ _ready: Promise<unknown>;
99
+ _sort: any;
100
+ _db!: MongoClient;
101
+ _mdb!: MongoDb;
102
+ _collection!: Collection;
103
+ _nextScanAt: any;
104
+ _processInterval: any;
105
+
106
+ cancel!: typeof cancel;
107
+ close!: typeof close;
108
+ create!: typeof create;
109
+ database!: typeof database;
110
+ db_init!: typeof dbInit;
111
+ defaultConcurrency!: typeof defaultConcurrency;
112
+ defaultLockLifetime!: typeof defaultLockLifetime;
113
+ defaultLockLimit!: typeof defaultLockLimit;
114
+ define!: typeof define;
115
+ disable!: typeof disable;
116
+ enable!: typeof enable;
117
+ every!: typeof every;
118
+ jobs!: typeof jobs;
119
+ lockLimit!: typeof lockLimit;
120
+ maxConcurrency!: typeof maxConcurrency;
121
+ mongo!: typeof mongo;
122
+ name!: typeof name;
123
+ now!: typeof now;
124
+ processEvery!: typeof processEvery;
125
+ purge!: typeof purge;
126
+ saveJob!: typeof saveJob;
127
+ schedule!: typeof schedule;
128
+ sort!: typeof sort;
129
+ start!: typeof start;
130
+ stop!: typeof stop;
131
+ drain!: typeof drain;
132
+
133
+ /**
134
+ * Constructs a new Agenda object.
135
+ * @param config Optional configuration to initialize the Agenda.
136
+ * @param cb Optional callback called with the MongoDB collection.
137
+ */
138
+ constructor(
139
+ config: AgendaConfig = {},
140
+ cb?: (
141
+ error: AnyError | undefined,
142
+ collection: Collection<any> | null
143
+ ) => void
144
+ ) {
145
+ super();
146
+
147
+ this._name = config.name;
148
+ this._processEvery = (humanInterval(config.processEvery) ??
149
+ humanInterval("5 seconds")) as number; // eslint-disable-line @typescript-eslint/non-nullable-type-assertion-style
150
+ this._defaultConcurrency = config.defaultConcurrency || 5;
151
+ this._maxConcurrency = config.maxConcurrency || 20;
152
+ this._defaultLockLimit = config.defaultLockLimit || 0;
153
+ this._lockLimit = config.lockLimit || 0;
154
+ this._definitions = {};
155
+ this._runningJobs = [];
156
+ this._lockedJobs = [];
157
+ this._jobQueue = new JobProcessingQueue();
158
+ this._defaultLockLifetime = config.defaultLockLifetime || 10 * 60 * 1000; // 10 minute default lockLifetime
159
+ this._sort = config.sort || { nextRunAt: 1, priority: -1 };
160
+ this._indices = {
161
+ name: 1,
162
+ ...this._sort,
163
+ priority: -1,
164
+ lockedAt: 1,
165
+ nextRunAt: 1,
166
+ disabled: 1,
167
+ };
168
+ this._disableAutoIndex = config.disableAutoIndex === true;
169
+
170
+ this._isLockingOnTheFly = false;
171
+ this._isJobQueueFilling = new Map<string, boolean>();
172
+ this._jobsToLock = [];
173
+ this._ready = new Promise((resolve) => {
174
+ this.once("ready", resolve);
175
+ });
176
+
177
+ if (config.mongo) {
178
+ this.mongo(
179
+ config.mongo,
180
+ config.db ? config.db.collection : undefined,
181
+ cb
182
+ ); // @ts-expect-error // the documentation shows it should be correct: http://mongodb.github.io/node-mongodb-native/3.6/api/Db.html
183
+ if (config.mongo.s && config.mongo.topology && config.mongo.topology.s) {
184
+ this._mongoUseUnifiedTopology = Boolean(
185
+ // @ts-expect-error
186
+ config.mongo.topology.s.options.useUnifiedTopology
187
+ );
188
+ }
189
+ } else if (config.db) {
190
+ this.database(
191
+ config.db.address,
192
+ config.db.collection,
193
+ config.db.options,
194
+ cb
195
+ );
196
+ }
197
+ }
198
+ }
199
+
200
+ Agenda.prototype.cancel = cancel;
201
+ Agenda.prototype.close = close;
202
+ Agenda.prototype.create = create;
203
+ Agenda.prototype.database = database;
204
+ Agenda.prototype.db_init = dbInit;
205
+ Agenda.prototype.defaultConcurrency = defaultConcurrency;
206
+ Agenda.prototype.defaultLockLifetime = defaultLockLifetime;
207
+ Agenda.prototype.defaultLockLimit = defaultLockLimit;
208
+ Agenda.prototype.define = define;
209
+ Agenda.prototype.disable = disable;
210
+ Agenda.prototype.enable = enable;
211
+ Agenda.prototype.every = every;
212
+ Agenda.prototype.jobs = jobs;
213
+ Agenda.prototype.lockLimit = lockLimit;
214
+ Agenda.prototype.maxConcurrency = maxConcurrency;
215
+ Agenda.prototype.mongo = mongo;
216
+ Agenda.prototype.name = name;
217
+ Agenda.prototype.now = now;
218
+ Agenda.prototype.processEvery = processEvery;
219
+ Agenda.prototype.purge = purge;
220
+ Agenda.prototype.saveJob = saveJob;
221
+ Agenda.prototype.schedule = schedule;
222
+ Agenda.prototype.sort = sort;
223
+ Agenda.prototype.start = start;
224
+ Agenda.prototype.stop = stop;
225
+ Agenda.prototype.drain = drain;
226
+
227
+ export { Agenda };
@@ -0,0 +1,99 @@
1
+ import { Job } from "../job";
2
+
3
+ /**
4
+ * @class
5
+ * @param {Object} args - Job Options
6
+ * @property {Object} agenda - The Agenda instance
7
+ * @property {Object} attrs
8
+ */
9
+ class JobProcessingQueue {
10
+ pop!: () => Job | undefined;
11
+ push!: (job: Job) => void;
12
+ insert!: (job: Job) => void;
13
+ returnNextConcurrencyFreeJob!: (agendaDefinitions: any) => Job;
14
+
15
+ protected _queue: Job[];
16
+
17
+ constructor() {
18
+ this._queue = [];
19
+ }
20
+
21
+ get length(): number {
22
+ return this._queue.length;
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Pops and returns last queue element (next job to be processed) without checking concurrency.
28
+ * @returns Next Job to be processed
29
+ */
30
+ JobProcessingQueue.prototype.pop = function (this: JobProcessingQueue) {
31
+ return this._queue.pop();
32
+ };
33
+
34
+ /**
35
+ * Inserts job in first queue position
36
+ * @param job job to add to queue
37
+ */
38
+ JobProcessingQueue.prototype.push = function (
39
+ this: JobProcessingQueue,
40
+ job: Job
41
+ ) {
42
+ this._queue.push(job);
43
+ };
44
+
45
+ /**
46
+ * Inserts job in queue where it will be order from left to right in decreasing
47
+ * order of nextRunAt and priority (in case of same nextRunAt), if all values
48
+ * are even the first jobs to be introduced will have priority
49
+ * @param job job to add to queue
50
+ */
51
+ JobProcessingQueue.prototype.insert = function (
52
+ this: JobProcessingQueue,
53
+ job: Job
54
+ ) {
55
+ const matchIndex = this._queue.findIndex((element) => {
56
+ if (element.attrs.nextRunAt!.getTime() <= job.attrs.nextRunAt!.getTime()) {
57
+ if (
58
+ element.attrs.nextRunAt!.getTime() === job.attrs.nextRunAt!.getTime()
59
+ ) {
60
+ if (element.attrs.priority >= job.attrs.priority) {
61
+ return true;
62
+ }
63
+ } else {
64
+ return true;
65
+ }
66
+ }
67
+
68
+ return false;
69
+ });
70
+
71
+ if (matchIndex === -1) {
72
+ this._queue.push(job);
73
+ } else {
74
+ this._queue.splice(matchIndex, 0, job);
75
+ }
76
+ };
77
+
78
+ /**
79
+ * Returns (does not pop, element remains in queue) first element (always from the right)
80
+ * that can be processed (not blocked by concurrency execution)
81
+ * @param agendaDefinitions job to add to queue
82
+ * @returns Next Job to be processed
83
+ */
84
+ JobProcessingQueue.prototype.returnNextConcurrencyFreeJob = function (
85
+ this: JobProcessingQueue,
86
+ agendaDefinitions: any
87
+ ) {
88
+ let next;
89
+ for (next = this._queue.length - 1; next > 0; next -= 1) {
90
+ const def = agendaDefinitions[this._queue[next].attrs.name];
91
+ if (def.concurrency > def.running) {
92
+ break;
93
+ }
94
+ }
95
+
96
+ return this._queue[next];
97
+ };
98
+
99
+ export { JobProcessingQueue };