@cedarjs/jobs 7.1.0 → 7.1.1-next.469
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/adapters/BaseAdapter/BaseAdapter.d.ts +133 -0
- package/dist/adapters/BaseAdapter/BaseAdapter.d.ts.map +1 -1
- package/dist/adapters/BaseAdapter/BaseAdapter.js +6 -0
- package/dist/adapters/PrismaAdapter/PrismaAdapter.d.ts +67 -3
- package/dist/adapters/PrismaAdapter/PrismaAdapter.d.ts.map +1 -1
- package/dist/adapters/PrismaAdapter/PrismaAdapter.js +378 -59
- package/dist/adapters/PrismaAdapter/errors.d.ts +14 -0
- package/dist/adapters/PrismaAdapter/errors.d.ts.map +1 -1
- package/dist/adapters/PrismaAdapter/errors.js +17 -1
- package/dist/bins/cedar-jobs-worker.d.ts +11 -0
- package/dist/bins/cedar-jobs-worker.d.ts.map +1 -1
- package/dist/bins/cedar-jobs-worker.js +12 -1
- package/dist/bins/cedar-jobs.d.ts +25 -1
- package/dist/bins/cedar-jobs.d.ts.map +1 -1
- package/dist/bins/cedar-jobs.js +104 -3
- package/dist/consts.d.ts +9 -0
- package/dist/consts.d.ts.map +1 -1
- package/dist/consts.js +7 -1
- package/dist/core/Executor.d.ts.map +1 -1
- package/dist/core/Executor.js +11 -4
- package/dist/core/JobManager.d.ts +76 -0
- package/dist/core/JobManager.d.ts.map +1 -1
- package/dist/core/JobManager.js +210 -1
- package/dist/core/Scheduler.d.ts.map +1 -1
- package/dist/core/Scheduler.js +16 -3
- package/dist/core/Worker.d.ts +8 -0
- package/dist/core/Worker.d.ts.map +1 -1
- package/dist/core/Worker.js +46 -1
- package/dist/errors.d.ts +54 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +60 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -1
- package/dist/loaders.d.ts +29 -0
- package/dist/loaders.d.ts.map +1 -1
- package/dist/loaders.js +48 -1
- package/dist/types.d.ts +101 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +15 -9
package/dist/bins/cedar-jobs.js
CHANGED
|
@@ -19,19 +19,100 @@ const parseArgs = (argv) => {
|
|
|
19
19
|
}
|
|
20
20
|
const parsed = yargs(commandString).usage(
|
|
21
21
|
"Starts the CedarJS Jobs runner to process background jobs\n\nUsage: cedar jobs <command> [options]"
|
|
22
|
-
).command("work", "Start a worker and process jobs").command("workoff", "Start a worker and exit after all jobs processed").command("start", "Start workers in daemon mode").command("stop", "Stop any daemonized job workers").command("restart", "Stop and start any daemonized job workers").command("clear", "Clear the job queue").
|
|
22
|
+
).command("work", "Start a worker and process jobs").command("workoff", "Start a worker and exit after all jobs processed").command("start", "Start workers in daemon mode").command("stop", "Stop any daemonized job workers").command("restart", "Stop and start any daemonized job workers").command("clear", "Clear the job queue").command("pause <id>", "Pause a job, so no worker picks it up").command("resume <id>", "Resume a paused job").command(
|
|
23
|
+
"list",
|
|
24
|
+
"List the jobs that are waiting to run, running or paused, with their ids"
|
|
25
|
+
).option("adapter", {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "pause, resume, list: the adapter that stores the jobs, when the JobManager has several"
|
|
28
|
+
}).option("limit", {
|
|
29
|
+
type: "number",
|
|
30
|
+
default: 100,
|
|
31
|
+
description: "list: the most jobs to list"
|
|
32
|
+
}).demandCommand(1, "You must specify a mode to start in").example(
|
|
23
33
|
"cedar jobs work",
|
|
24
34
|
"Start the job workers using the job config and work on jobs until manually stopped"
|
|
25
35
|
).example(
|
|
26
36
|
"cedar jobs start",
|
|
27
37
|
"Start the job workers using the job config and detach, running in daemon mode"
|
|
38
|
+
).example(
|
|
39
|
+
"cedar jobs pause 42",
|
|
40
|
+
"Stop job 42 from running until `cedar jobs resume 42`"
|
|
28
41
|
).help().parse(commandString, (_err, _argv, output) => {
|
|
29
42
|
if (output) {
|
|
30
43
|
const newOutput = output.replaceAll("cedar-jobs.js", "cedar jobs");
|
|
31
44
|
console.log(newOutput);
|
|
32
45
|
}
|
|
33
46
|
});
|
|
34
|
-
return {
|
|
47
|
+
return {
|
|
48
|
+
command: parsed._[0],
|
|
49
|
+
id: parseJobId(parsed.id),
|
|
50
|
+
adapter: typeof parsed.adapter === "string" ? parsed.adapter : void 0,
|
|
51
|
+
limit: typeof parsed.limit === "number" ? parsed.limit : 100
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
const parseJobId = (value) => {
|
|
55
|
+
if (typeof value === "number") {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
if (typeof value !== "string" || value === "") {
|
|
59
|
+
return void 0;
|
|
60
|
+
}
|
|
61
|
+
const number = Number(value);
|
|
62
|
+
return /^\d+$/.test(value) && Number.isSafeInteger(number) && String(number) === value ? number : value;
|
|
63
|
+
};
|
|
64
|
+
const runPauseCommand = async ({
|
|
65
|
+
command,
|
|
66
|
+
id,
|
|
67
|
+
adapter,
|
|
68
|
+
limit = 100,
|
|
69
|
+
manager
|
|
70
|
+
}) => {
|
|
71
|
+
if (command === "list") {
|
|
72
|
+
const listed = await manager.listJobs({ adapter, limit });
|
|
73
|
+
if (listed.length === 0) {
|
|
74
|
+
return "No jobs are waiting to run";
|
|
75
|
+
}
|
|
76
|
+
const rows = [
|
|
77
|
+
["ID", "JOB", "QUEUE", "CRON", "STATUS"],
|
|
78
|
+
...listed.map((job) => [
|
|
79
|
+
String(job.id),
|
|
80
|
+
job.name,
|
|
81
|
+
job.queue,
|
|
82
|
+
job.cron ?? "-",
|
|
83
|
+
[
|
|
84
|
+
job.pausedAt ? `paused since ${job.pausedAt.toISOString()}` : "",
|
|
85
|
+
job.running ? "running" : "",
|
|
86
|
+
!job.pausedAt && !job.running ? `next run ${job.runAt?.toISOString() ?? "-"}` : ""
|
|
87
|
+
].filter(Boolean).join(", ")
|
|
88
|
+
])
|
|
89
|
+
];
|
|
90
|
+
const widths = rows[0].map(
|
|
91
|
+
(_, column) => Math.max(...rows.map((row) => row[column].length))
|
|
92
|
+
);
|
|
93
|
+
const lines = rows.map(
|
|
94
|
+
(row) => row.map(
|
|
95
|
+
(cell, column) => column === row.length - 1 ? cell : cell.padEnd(widths[column])
|
|
96
|
+
).join(" ")
|
|
97
|
+
);
|
|
98
|
+
if (listed.length === limit) {
|
|
99
|
+
lines.push(`(the first ${limit}, use --limit to see more)`);
|
|
100
|
+
}
|
|
101
|
+
return lines.join("\n");
|
|
102
|
+
}
|
|
103
|
+
if (id === void 0) {
|
|
104
|
+
throw new Error(`Say which job to ${command} by its id`);
|
|
105
|
+
}
|
|
106
|
+
if (command === "pause") {
|
|
107
|
+
const result2 = await manager.pauseJob(id, { adapter });
|
|
108
|
+
return result2.alreadyPaused ? `Job ${id} was already paused (since ${result2.pausedAt.toISOString()})` : `Paused job ${id}. It doesn't run until you resume it with \`yarn cedar jobs resume ${id}\``;
|
|
109
|
+
}
|
|
110
|
+
const result = await manager.resumeJob(id, { adapter });
|
|
111
|
+
const nextRun = result.runAt?.toISOString() ?? "-";
|
|
112
|
+
if (result.wasPaused && !result.runAt) {
|
|
113
|
+
return `Resumed job ${id}. Its next run time is set when the current run finishes`;
|
|
114
|
+
}
|
|
115
|
+
return result.wasPaused ? `Resumed job ${id}. Its next run is at ${nextRun}` : `Job ${id} isn't paused. Its next run is at ${nextRun}`;
|
|
35
116
|
};
|
|
36
117
|
const buildNumWorkers = (config) => {
|
|
37
118
|
const workers = [];
|
|
@@ -145,7 +226,7 @@ const findWorkerProcesses = async (id) => {
|
|
|
145
226
|
});
|
|
146
227
|
};
|
|
147
228
|
const main = async () => {
|
|
148
|
-
const { command } = parseArgs(process.argv);
|
|
229
|
+
const { command, id, adapter, limit } = parseArgs(process.argv);
|
|
149
230
|
let jobsConfig;
|
|
150
231
|
try {
|
|
151
232
|
jobsConfig = await loadJobsManager();
|
|
@@ -153,6 +234,23 @@ const main = async () => {
|
|
|
153
234
|
console.error(e);
|
|
154
235
|
process.exit(1);
|
|
155
236
|
}
|
|
237
|
+
if (command === "pause" || command === "resume" || command === "list") {
|
|
238
|
+
try {
|
|
239
|
+
console.log(
|
|
240
|
+
await runPauseCommand({
|
|
241
|
+
command,
|
|
242
|
+
id,
|
|
243
|
+
adapter,
|
|
244
|
+
limit,
|
|
245
|
+
manager: jobsConfig
|
|
246
|
+
})
|
|
247
|
+
);
|
|
248
|
+
} catch (e) {
|
|
249
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
return process.exit(0);
|
|
253
|
+
}
|
|
156
254
|
const workerConfig = jobsConfig.workers;
|
|
157
255
|
const numWorkers = buildNumWorkers(workerConfig);
|
|
158
256
|
const logger = jobsConfig.logger ?? DEFAULT_LOGGER;
|
|
@@ -206,6 +304,9 @@ if (process.env.NODE_ENV !== "test") {
|
|
|
206
304
|
export {
|
|
207
305
|
buildNumWorkers,
|
|
208
306
|
clearQueue,
|
|
307
|
+
parseArgs,
|
|
308
|
+
parseJobId,
|
|
309
|
+
runPauseCommand,
|
|
209
310
|
startWorkers,
|
|
210
311
|
stopWorkers
|
|
211
312
|
};
|
package/dist/consts.d.ts
CHANGED
|
@@ -30,6 +30,15 @@ export declare const DEFAULT_PRIORITY = 50;
|
|
|
30
30
|
export declare const DEFAULT_WAIT = 0;
|
|
31
31
|
export declare const DEFAULT_WAIT_UNTIL: null;
|
|
32
32
|
export declare const PROCESS_TITLE_PREFIX = "cedar-jobs-worker";
|
|
33
|
+
/** Job keys starting with this are reserved for keys Cedar creates itself */
|
|
34
|
+
export declare const RESERVED_KEY_PREFIX = "cedar:";
|
|
35
|
+
/**
|
|
36
|
+
* Keys of stored declared schedules start with this, followed by the job's
|
|
37
|
+
* name and the schedule's own key: `cedar:schedule:ReportJob:nightly`
|
|
38
|
+
*/
|
|
39
|
+
export declare const SCHEDULE_KEY_PREFIX = "cedar:schedule:";
|
|
40
|
+
/** The key of a job's schedule when the job has one and it has no `key` */
|
|
41
|
+
export declare const DEFAULT_SCHEDULE_KEY = "default";
|
|
33
42
|
export declare const DEFAULT_MODEL_NAME = "BackgroundJob";
|
|
34
43
|
/**
|
|
35
44
|
* The name of the exported variable from the jobs config file that contains
|
package/dist/consts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oBAAoB,KAAK,CAAA;AACtC,yBAAyB;AACzB,eAAO,MAAM,mBAAmB,QAAS,CAAA;AACzC;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAA;AAC1C,gBAAgB;AAChB,eAAO,MAAM,mBAAmB,IAAI,CAAA;AACpC;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,QAA0B,CAAA;AAErD,eAAO,MAAM,8BAA8B,OAAO,CAAA;AAClD,eAAO,MAAM,0BAA0B,QAAQ,CAAA;AAC/C,eAAO,MAAM,cAAc,SAAU,CAAA;AACrC,eAAO,MAAM,aAAa,YAAY,CAAA;AACtC,eAAO,MAAM,kBAAkB,MAAM,CAAA;AACrC,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAClC,eAAO,MAAM,YAAY,IAAI,CAAA;AAC7B,eAAO,MAAM,kBAAkB,MAAO,CAAA;AACtC,eAAO,MAAM,oBAAoB,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,oBAAoB,KAAK,CAAA;AACtC,yBAAyB;AACzB,eAAO,MAAM,mBAAmB,QAAS,CAAA;AACzC;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAA;AAC1C,gBAAgB;AAChB,eAAO,MAAM,mBAAmB,IAAI,CAAA;AACpC;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,QAA0B,CAAA;AAErD,eAAO,MAAM,8BAA8B,OAAO,CAAA;AAClD,eAAO,MAAM,0BAA0B,QAAQ,CAAA;AAC/C,eAAO,MAAM,cAAc,SAAU,CAAA;AACrC,eAAO,MAAM,aAAa,YAAY,CAAA;AACtC,eAAO,MAAM,kBAAkB,MAAM,CAAA;AACrC,eAAO,MAAM,gBAAgB,KAAK,CAAA;AAClC,eAAO,MAAM,YAAY,IAAI,CAAA;AAC7B,eAAO,MAAM,kBAAkB,MAAO,CAAA;AACtC,eAAO,MAAM,oBAAoB,sBAAsB,CAAA;AACvD,6EAA6E;AAC7E,eAAO,MAAM,mBAAmB,WAAW,CAAA;AAC3C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,oBAAoC,CAAA;AACpE,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,YAAY,CAAA;AAE7C,eAAO,MAAM,kBAAkB,kBAAkB,CAAA;AAEjD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,YAAY,CAAA;AAC7C;;;GAGG;AACH,eAAO,MAAM,mBAAmB,WAAW,CAAA"}
|
package/dist/consts.js
CHANGED
|
@@ -13,6 +13,9 @@ const DEFAULT_PRIORITY = 50;
|
|
|
13
13
|
const DEFAULT_WAIT = 0;
|
|
14
14
|
const DEFAULT_WAIT_UNTIL = null;
|
|
15
15
|
const PROCESS_TITLE_PREFIX = "cedar-jobs-worker";
|
|
16
|
+
const RESERVED_KEY_PREFIX = "cedar:";
|
|
17
|
+
const SCHEDULE_KEY_PREFIX = `${RESERVED_KEY_PREFIX}schedule:`;
|
|
18
|
+
const DEFAULT_SCHEDULE_KEY = "default";
|
|
16
19
|
const DEFAULT_MODEL_NAME = "BackgroundJob";
|
|
17
20
|
const DEFAULT_ADAPTER_NAME = "adapter";
|
|
18
21
|
const DEFAULT_LOGGER_NAME = "logger";
|
|
@@ -27,11 +30,14 @@ export {
|
|
|
27
30
|
DEFAULT_MODEL_NAME,
|
|
28
31
|
DEFAULT_PRIORITY,
|
|
29
32
|
DEFAULT_QUEUE,
|
|
33
|
+
DEFAULT_SCHEDULE_KEY,
|
|
30
34
|
DEFAULT_SLEEP_DELAY,
|
|
31
35
|
DEFAULT_WAIT,
|
|
32
36
|
DEFAULT_WAIT_UNTIL,
|
|
33
37
|
DEFAULT_WORK_QUEUE,
|
|
34
38
|
MAX_BACKOFF_MS,
|
|
35
39
|
MAX_RUNTIME_GRACE_PERIOD,
|
|
36
|
-
PROCESS_TITLE_PREFIX
|
|
40
|
+
PROCESS_TITLE_PREFIX,
|
|
41
|
+
RESERVED_KEY_PREFIX,
|
|
42
|
+
SCHEDULE_KEY_PREFIX
|
|
37
43
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Executor.d.ts","sourceRoot":"","sources":["../../src/core/Executor.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;
|
|
1
|
+
{"version":3,"file":"Executor.d.ts","sourceRoot":"","sources":["../../src/core/Executor.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAgBzE,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAKvD,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,WAAW,CAAA;IACpB,GAAG,EAAE,OAAO,CAAA;IACZ,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,uCAAuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6CAA6C;IAC7C,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,iDAAiD;IACjD,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,eAAO,MAAM,QAAQ;;;;;;CAMpB,CAAA;AAED,qBAAa,QAAQ;;IACnB,OAAO,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAA;IAClC,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,CAAA;IACnC,MAAM,EAAE,WAAW,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC9C,GAAG,EAAE,OAAO,CAAA;IACZ,WAAW,EAAE,WAAW,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAAA;IACxD,UAAU,EAAE,WAAW,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAA;IACtD,gBAAgB,EAAE,WAAW,CAAC,eAAe,CAAC,kBAAkB,CAAC,CAAC,CAAA;IAClE,oBAAoB,EAAE,WAAW,CAAC,eAAe,CAAC,sBAAsB,CAAC,CAAC,CAAA;gBAE9D,OAAO,EAAE,eAAe;IAsBpC,IAAI,aAAa,WAEhB;IAED;;;;;;;OAOG;IACG,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAuKjC;;;;OAIG;IACH,mBAAmB,CAAC,QAAQ,EAAE,MAAM;CAGrC"}
|
package/dist/core/Executor.js
CHANGED
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
DEFAULT_DELETE_FAILED_JOBS,
|
|
6
6
|
DEFAULT_DELETE_SUCCESSFUL_JOBS,
|
|
7
7
|
DEFAULT_LOGGER,
|
|
8
|
-
MAX_BACKOFF_MS
|
|
8
|
+
MAX_BACKOFF_MS,
|
|
9
|
+
SCHEDULE_KEY_PREFIX
|
|
9
10
|
} from "../consts.js";
|
|
10
11
|
import {
|
|
11
12
|
AdapterRequiredError,
|
|
@@ -134,12 +135,18 @@ class Executor {
|
|
|
134
135
|
* called only after `failure()` has recorded that (a throwing `failure()`
|
|
135
136
|
* is logged by `#adapterWrite()` instead). A permanently failed recurring
|
|
136
137
|
* (cron) job is not rescheduled, so its schedule stops until the job is
|
|
137
|
-
* scheduled again
|
|
138
|
-
* restart the
|
|
138
|
+
* scheduled again (a declared schedule: until the workers restart and sync
|
|
139
|
+
* it). That is logged at error level, together with how to restart the
|
|
140
|
+
* schedule
|
|
139
141
|
*/
|
|
140
142
|
#logPermanentFailure(reason) {
|
|
141
143
|
const message = `[CedarJS Jobs] Failed job ${this.jobIdentifier}: ${reason}`;
|
|
142
|
-
if (this.job.cron) {
|
|
144
|
+
if (this.job.cron && this.job.key?.startsWith(SCHEDULE_KEY_PREFIX)) {
|
|
145
|
+
this.logger.error(
|
|
146
|
+
this.job,
|
|
147
|
+
`${message}. Its declared schedule (cron: '${this.job.cron}') has stopped until the job workers restart, which recreates it`
|
|
148
|
+
);
|
|
149
|
+
} else if (this.job.cron) {
|
|
143
150
|
this.logger.error(
|
|
144
151
|
this.job,
|
|
145
152
|
`${message}. Its recurring schedule (cron: '${this.job.cron}') has stopped and the job will not run again. To restart it, schedule the job again with \`later(job, args, { cron: '${this.job.cron}' })\``
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
+
import type { ListedJob, PauseResult, ResumeResult, SyncSchedulesResult } from '../adapters/BaseAdapter/BaseAdapter.js';
|
|
1
2
|
import type { Adapters, BasicLogger, CreateSchedulerArgs, CreateSchedulerConfig, Job, JobDefinition, JobManagerConfig, QueueNames, WorkerConfig } from '../types.js';
|
|
2
3
|
import type { WorkerOptions } from './Worker.js';
|
|
3
4
|
import { Worker } from './Worker.js';
|
|
5
|
+
/** Which adapter `pauseJob()`, `resumeJob()` and `listJobs()` use */
|
|
6
|
+
export interface JobAdapterOptions<TAdapters extends Adapters> {
|
|
7
|
+
/**
|
|
8
|
+
* The name of the adapter that stores the job. Can be left out when the
|
|
9
|
+
* `JobManager` has a single adapter
|
|
10
|
+
*/
|
|
11
|
+
adapter?: keyof TAdapters;
|
|
12
|
+
}
|
|
4
13
|
export interface CreateWorkerArgs {
|
|
5
14
|
index: number;
|
|
6
15
|
workoff: WorkerOptions['workoff'];
|
|
@@ -8,15 +17,82 @@ export interface CreateWorkerArgs {
|
|
|
8
17
|
processName: string;
|
|
9
18
|
}
|
|
10
19
|
export declare class JobManager<TAdapters extends Adapters, TQueues extends QueueNames, TLogger extends BasicLogger> {
|
|
20
|
+
#private;
|
|
11
21
|
adapters: TAdapters;
|
|
12
22
|
queues: TQueues;
|
|
13
23
|
logger: TLogger;
|
|
14
24
|
workers: WorkerConfig<TAdapters, TQueues>[];
|
|
25
|
+
scheduleAdapter: keyof TAdapters | undefined;
|
|
15
26
|
constructor(config: JobManagerConfig<TAdapters, TQueues extends string[] ? never : TQueues, TLogger>);
|
|
16
27
|
createScheduler<TAdapterName extends keyof TAdapters>(schedulerConfig: CreateSchedulerConfig<TAdapters, TAdapterName>): (<TJob extends Job<TQueues, any[]>>(job: TJob, ...argsAndOptions: CreateSchedulerArgs<TJob>) => Promise<Awaited<ReturnType<TAdapters[TAdapterName]["schedule"]>>>) & {
|
|
17
28
|
cancel: (jobId: string | number) => Promise<boolean>;
|
|
18
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Pauses a job, without a deploy: no worker picks it up until it's resumed
|
|
32
|
+
* with `resumeJob()`. Works for any job that's waiting to run, recurring
|
|
33
|
+
* (`cron`) or not. A run that's in progress finishes, and a recurring job's
|
|
34
|
+
* next run stays paused.
|
|
35
|
+
*
|
|
36
|
+
* The pause is stored on the job's row. Code that deletes and recreates
|
|
37
|
+
* its recurring jobs (like a script that schedules them on every deploy)
|
|
38
|
+
* creates new rows, which aren't paused.
|
|
39
|
+
*
|
|
40
|
+
* Also available as `yarn cedar jobs pause <id>`.
|
|
41
|
+
*
|
|
42
|
+
* @param jobId The job's id, as returned by `later()` (or listed by
|
|
43
|
+
* `listJobs()` and `yarn cedar jobs list`)
|
|
44
|
+
*
|
|
45
|
+
* @returns Whether the job was paused already, and since when it's paused
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* await jobs.pauseJob(42)
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
pauseJob(jobId: string | number, options?: JobAdapterOptions<TAdapters>): Promise<PauseResult>;
|
|
53
|
+
/**
|
|
54
|
+
* Resumes a job paused with `pauseJob()`.
|
|
55
|
+
*
|
|
56
|
+
* - A recurring (`cron`) job runs next the next time its cron matches from
|
|
57
|
+
* now. Runs missed while it was paused are not made up for.
|
|
58
|
+
* - A one-off job keeps its run time, so it runs as soon as a worker is
|
|
59
|
+
* free when that time has passed.
|
|
60
|
+
*
|
|
61
|
+
* Resuming a job that isn't paused changes nothing. Also available as
|
|
62
|
+
* `yarn cedar jobs resume <id>`.
|
|
63
|
+
*
|
|
64
|
+
* @returns Whether the job was paused, and when it runs next
|
|
65
|
+
*/
|
|
66
|
+
resumeJob(jobId: string | number, options?: JobAdapterOptions<TAdapters>): Promise<ResumeResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Lists the jobs that are waiting to run, running or paused, the ones that
|
|
69
|
+
* run next first, to find a job's id. Completed and failed jobs are left
|
|
70
|
+
* out. Also available as `yarn cedar jobs list`.
|
|
71
|
+
*
|
|
72
|
+
* @param options.limit The most jobs to list. Default: 100
|
|
73
|
+
*/
|
|
74
|
+
listJobs(options?: JobAdapterOptions<TAdapters> & {
|
|
75
|
+
limit?: number;
|
|
76
|
+
}): Promise<ListedJob[]>;
|
|
19
77
|
createJob<TArgs extends unknown[]>(jobDefinition: JobDefinition<TQueues, TArgs>): Job<TQueues, TArgs>;
|
|
78
|
+
/**
|
|
79
|
+
* Makes the recurring schedules stored by the schedule adapter match the
|
|
80
|
+
* `schedules` the jobs declare in `createJob()`: adds missing ones, updates
|
|
81
|
+
* changed ones and removes the ones that are no longer declared. Safe to
|
|
82
|
+
* run from several processes at once.
|
|
83
|
+
*
|
|
84
|
+
* Every job worker calls this when it starts, so there's usually no need
|
|
85
|
+
* to call it yourself.
|
|
86
|
+
*
|
|
87
|
+
* @param jobs The jobs to read declared schedules from. Defaults to every
|
|
88
|
+
* job in the project (see `loadAllJobs()`), which is what the sync has to
|
|
89
|
+
* see to know which stored schedules are no longer declared.
|
|
90
|
+
*
|
|
91
|
+
* @returns What the sync did, or `undefined` if there was nothing to sync:
|
|
92
|
+
* no job declares schedules, and either the adapter can't store them or
|
|
93
|
+
* there are several adapters and no `scheduleAdapter` names one
|
|
94
|
+
*/
|
|
95
|
+
syncSchedules(jobs?: Job<TQueues, any[]>[]): Promise<SyncSchedulesResult | undefined>;
|
|
20
96
|
createWorker({ index, workoff, clear, processName }: CreateWorkerArgs): Worker;
|
|
21
97
|
}
|
|
22
98
|
//# sourceMappingURL=JobManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JobManager.d.ts","sourceRoot":"","sources":["../../src/core/JobManager.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"JobManager.d.ts","sourceRoot":"","sources":["../../src/core/JobManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,SAAS,EACT,WAAW,EACX,YAAY,EAEZ,mBAAmB,EACpB,MAAM,wCAAwC,CAAA;AAU/C,OAAO,KAAK,EACV,QAAQ,EACR,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,GAAG,EACH,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,YAAY,EACb,MAAM,aAAa,CAAA;AAIpB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,qEAAqE;AACrE,MAAM,WAAW,iBAAiB,CAAC,SAAS,SAAS,QAAQ;IAC3D;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,SAAS,CAAA;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAA;IACjC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;IAC7B,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,qBAAa,UAAU,CACrB,SAAS,SAAS,QAAQ,EAC1B,OAAO,SAAS,UAAU,EAC1B,OAAO,SAAS,WAAW;;IAE3B,QAAQ,EAAE,SAAS,CAAA;IACnB,MAAM,EAAE,OAAO,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,CAAA;IAC3C,eAAe,EAAE,MAAM,SAAS,GAAG,SAAS,CAAA;gBAG1C,MAAM,EAAE,gBAAgB,CACtB,SAAS,EAGT,OAAO,SAAS,MAAM,EAAE,GAAG,KAAK,GAAG,OAAO,EAC1C,OAAO,CACR;IASH,eAAe,CAAC,YAAY,SAAS,MAAM,SAAS,EAClD,eAAe,EAAE,qBAAqB,CAAC,SAAS,EAAE,YAAY,CAAC,KAO7C,IAAI,SAAS,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAC3C,IAAI,qBACU,mBAAmB,CAAC,IAAI,CAAC;wBAc5B,MAAM,GAAG,MAAM;;IAInC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,QAAQ,CACZ,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,OAAO,GAAE,iBAAiB,CAAC,SAAS,CAAM,GACzC,OAAO,CAAC,WAAW,CAAC;IAmBvB;;;;;;;;;;;;OAYG;IACG,SAAS,CACb,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,OAAO,GAAE,iBAAiB,CAAC,SAAS,CAAM,GACzC,OAAO,CAAC,YAAY,CAAC;IAsBxB;;;;;;OAMG;IACG,QAAQ,CACZ,OAAO,GAAE,iBAAiB,CAAC,SAAS,CAAC,GAAG;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO,GAC9D,OAAO,CAAC,SAAS,EAAE,CAAC;IA2BvB,SAAS,CAAC,KAAK,SAAS,OAAO,EAAE,EAC/B,aAAa,EAAE,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,GAC3C,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC;IAOtB;;;;;;;;;;;;;;;;OAgBG;IACG,aAAa,CACjB,IAAI,CAAC,EAAE,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE,GAC3B,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAgJ3C,YAAY,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,gBAAgB;CAqBtE"}
|
package/dist/core/JobManager.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DEFAULT_SCHEDULE_KEY, SCHEDULE_KEY_PREFIX } from "../consts.js";
|
|
2
|
+
import {
|
|
3
|
+
AdapterNameRequiredError,
|
|
4
|
+
AdapterNotFoundError,
|
|
5
|
+
PauseNotImplementedError,
|
|
6
|
+
ScheduleConfigError,
|
|
7
|
+
SchedulesNotSupportedError
|
|
8
|
+
} from "../errors.js";
|
|
9
|
+
import { loadAllJobs } from "../loaders.js";
|
|
10
|
+
import { nextCronRunAt } from "../util.js";
|
|
2
11
|
import { Scheduler } from "./Scheduler.js";
|
|
3
12
|
import { Worker } from "./Worker.js";
|
|
4
13
|
class JobManager {
|
|
@@ -6,11 +15,13 @@ class JobManager {
|
|
|
6
15
|
queues;
|
|
7
16
|
logger;
|
|
8
17
|
workers;
|
|
18
|
+
scheduleAdapter;
|
|
9
19
|
constructor(config) {
|
|
10
20
|
this.adapters = config.adapters;
|
|
11
21
|
this.queues = config.queues;
|
|
12
22
|
this.logger = config.logger;
|
|
13
23
|
this.workers = config.workers;
|
|
24
|
+
this.scheduleAdapter = config.scheduleAdapter;
|
|
14
25
|
}
|
|
15
26
|
createScheduler(schedulerConfig) {
|
|
16
27
|
const scheduler = new Scheduler({
|
|
@@ -28,9 +39,207 @@ class JobManager {
|
|
|
28
39
|
cancel: (jobId) => scheduler.cancel(jobId)
|
|
29
40
|
});
|
|
30
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Pauses a job, without a deploy: no worker picks it up until it's resumed
|
|
44
|
+
* with `resumeJob()`. Works for any job that's waiting to run, recurring
|
|
45
|
+
* (`cron`) or not. A run that's in progress finishes, and a recurring job's
|
|
46
|
+
* next run stays paused.
|
|
47
|
+
*
|
|
48
|
+
* The pause is stored on the job's row. Code that deletes and recreates
|
|
49
|
+
* its recurring jobs (like a script that schedules them on every deploy)
|
|
50
|
+
* creates new rows, which aren't paused.
|
|
51
|
+
*
|
|
52
|
+
* Also available as `yarn cedar jobs pause <id>`.
|
|
53
|
+
*
|
|
54
|
+
* @param jobId The job's id, as returned by `later()` (or listed by
|
|
55
|
+
* `listJobs()` and `yarn cedar jobs list`)
|
|
56
|
+
*
|
|
57
|
+
* @returns Whether the job was paused already, and since when it's paused
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* await jobs.pauseJob(42)
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
async pauseJob(jobId, options = {}) {
|
|
65
|
+
const adapter = this.#adapter(options.adapter);
|
|
66
|
+
if (!adapter.pause) {
|
|
67
|
+
throw new PauseNotImplementedError("pause");
|
|
68
|
+
}
|
|
69
|
+
const result = await adapter.pause({ jobId });
|
|
70
|
+
this.logger.info(
|
|
71
|
+
result.alreadyPaused ? `[CedarJS Jobs] Job ${jobId} was already paused (since ${result.pausedAt.toISOString()})` : `[CedarJS Jobs] Paused job ${jobId}`
|
|
72
|
+
);
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Resumes a job paused with `pauseJob()`.
|
|
77
|
+
*
|
|
78
|
+
* - A recurring (`cron`) job runs next the next time its cron matches from
|
|
79
|
+
* now. Runs missed while it was paused are not made up for.
|
|
80
|
+
* - A one-off job keeps its run time, so it runs as soon as a worker is
|
|
81
|
+
* free when that time has passed.
|
|
82
|
+
*
|
|
83
|
+
* Resuming a job that isn't paused changes nothing. Also available as
|
|
84
|
+
* `yarn cedar jobs resume <id>`.
|
|
85
|
+
*
|
|
86
|
+
* @returns Whether the job was paused, and when it runs next
|
|
87
|
+
*/
|
|
88
|
+
async resumeJob(jobId, options = {}) {
|
|
89
|
+
const adapter = this.#adapter(options.adapter);
|
|
90
|
+
if (!adapter.resume) {
|
|
91
|
+
throw new PauseNotImplementedError("resume");
|
|
92
|
+
}
|
|
93
|
+
const result = await adapter.resume({ jobId });
|
|
94
|
+
const nextRun = result.runAt ? `next run at ${result.runAt.toISOString()}` : "next run time set when the current run finishes";
|
|
95
|
+
this.logger.info(
|
|
96
|
+
result.wasPaused ? `[CedarJS Jobs] Resumed job ${jobId}, ${nextRun}` : `[CedarJS Jobs] Job ${jobId} isn't paused`
|
|
97
|
+
);
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Lists the jobs that are waiting to run, running or paused, the ones that
|
|
102
|
+
* run next first, to find a job's id. Completed and failed jobs are left
|
|
103
|
+
* out. Also available as `yarn cedar jobs list`.
|
|
104
|
+
*
|
|
105
|
+
* @param options.limit The most jobs to list. Default: 100
|
|
106
|
+
*/
|
|
107
|
+
async listJobs(options = {}) {
|
|
108
|
+
const adapter = this.#adapter(options.adapter);
|
|
109
|
+
if (!adapter.list) {
|
|
110
|
+
throw new PauseNotImplementedError("list");
|
|
111
|
+
}
|
|
112
|
+
return adapter.list({ limit: options.limit ?? 100 });
|
|
113
|
+
}
|
|
114
|
+
#adapter(name) {
|
|
115
|
+
const names = Object.keys(this.adapters);
|
|
116
|
+
if (name === void 0 && names.length !== 1) {
|
|
117
|
+
throw new AdapterNameRequiredError(names);
|
|
118
|
+
}
|
|
119
|
+
const adapterName = name ?? names[0];
|
|
120
|
+
const adapter = this.adapters[adapterName];
|
|
121
|
+
if (!adapter) {
|
|
122
|
+
throw new AdapterNotFoundError(String(adapterName));
|
|
123
|
+
}
|
|
124
|
+
return adapter;
|
|
125
|
+
}
|
|
31
126
|
createJob(jobDefinition) {
|
|
32
127
|
return jobDefinition;
|
|
33
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Makes the recurring schedules stored by the schedule adapter match the
|
|
131
|
+
* `schedules` the jobs declare in `createJob()`: adds missing ones, updates
|
|
132
|
+
* changed ones and removes the ones that are no longer declared. Safe to
|
|
133
|
+
* run from several processes at once.
|
|
134
|
+
*
|
|
135
|
+
* Every job worker calls this when it starts, so there's usually no need
|
|
136
|
+
* to call it yourself.
|
|
137
|
+
*
|
|
138
|
+
* @param jobs The jobs to read declared schedules from. Defaults to every
|
|
139
|
+
* job in the project (see `loadAllJobs()`), which is what the sync has to
|
|
140
|
+
* see to know which stored schedules are no longer declared.
|
|
141
|
+
*
|
|
142
|
+
* @returns What the sync did, or `undefined` if there was nothing to sync:
|
|
143
|
+
* no job declares schedules, and either the adapter can't store them or
|
|
144
|
+
* there are several adapters and no `scheduleAdapter` names one
|
|
145
|
+
*/
|
|
146
|
+
async syncSchedules(jobs) {
|
|
147
|
+
const allJobs = jobs ?? await loadAllJobs();
|
|
148
|
+
const declaresSchedules = allJobs.some(
|
|
149
|
+
(job) => (job.schedules?.length ?? 0) > 0
|
|
150
|
+
);
|
|
151
|
+
if (!declaresSchedules && this.scheduleAdapter === void 0 && Object.keys(this.adapters).length !== 1) {
|
|
152
|
+
return void 0;
|
|
153
|
+
}
|
|
154
|
+
const adapterName = this.#scheduleAdapterName();
|
|
155
|
+
const adapter = this.adapters[adapterName];
|
|
156
|
+
if (!adapter) {
|
|
157
|
+
throw new AdapterNotFoundError(String(adapterName));
|
|
158
|
+
}
|
|
159
|
+
const schedules = this.#scheduleDefinitions(allJobs, adapter);
|
|
160
|
+
if (!adapter.syncSchedules) {
|
|
161
|
+
if (schedules.length > 0) {
|
|
162
|
+
throw new SchedulesNotSupportedError(String(adapterName));
|
|
163
|
+
}
|
|
164
|
+
return void 0;
|
|
165
|
+
}
|
|
166
|
+
const result = await adapter.syncSchedules({
|
|
167
|
+
schedules,
|
|
168
|
+
keyPrefix: SCHEDULE_KEY_PREFIX
|
|
169
|
+
});
|
|
170
|
+
const changes = [
|
|
171
|
+
...result.created.map((key) => `created ${key}`),
|
|
172
|
+
...result.updated.map((key) => `updated ${key}`),
|
|
173
|
+
...result.removed.map((key) => `removed ${key}`)
|
|
174
|
+
];
|
|
175
|
+
if (changes.length > 0) {
|
|
176
|
+
this.logger.info(
|
|
177
|
+
`[CedarJS Jobs] Synced declared schedules: ${changes.join(", ")}`
|
|
178
|
+
);
|
|
179
|
+
} else {
|
|
180
|
+
this.logger.debug(
|
|
181
|
+
`[CedarJS Jobs] Declared schedules are up to date (${result.unchanged.length})`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
return result;
|
|
185
|
+
}
|
|
186
|
+
#scheduleAdapterName() {
|
|
187
|
+
if (this.scheduleAdapter !== void 0) {
|
|
188
|
+
return this.scheduleAdapter;
|
|
189
|
+
}
|
|
190
|
+
const names = Object.keys(this.adapters);
|
|
191
|
+
if (names.length !== 1) {
|
|
192
|
+
throw new ScheduleConfigError(
|
|
193
|
+
`Set \`scheduleAdapter\` in your JobManager config to the adapter that stores the schedules jobs declare. It can only be left out when there is exactly one adapter (found: ${names.join(", ") || "none"})`
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
return names[0];
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Turns the jobs' declared schedules into what the adapter stores, and
|
|
200
|
+
* checks them: every schedule needs a key when its job has more than one,
|
|
201
|
+
* identities must be unique, and every cron expression must parse
|
|
202
|
+
*/
|
|
203
|
+
#scheduleDefinitions(jobs, adapter) {
|
|
204
|
+
const scheduler = new Scheduler({ adapter, logger: this.logger });
|
|
205
|
+
const definitions = [];
|
|
206
|
+
const owners = /* @__PURE__ */ new Map();
|
|
207
|
+
for (const job of jobs) {
|
|
208
|
+
const jobSchedules = job.schedules ?? [];
|
|
209
|
+
for (const schedule of jobSchedules) {
|
|
210
|
+
const scheduleKey = schedule.key ?? DEFAULT_SCHEDULE_KEY;
|
|
211
|
+
if (schedule.key === void 0 && jobSchedules.length > 1) {
|
|
212
|
+
throw new ScheduleConfigError(
|
|
213
|
+
`${job.name} declares ${jobSchedules.length} schedules, so each of them needs a \`key\` to tell them apart`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
const key = `${SCHEDULE_KEY_PREFIX}${job.name}:${scheduleKey}`;
|
|
217
|
+
const owner = `${job.path} (${job.name})`;
|
|
218
|
+
const otherOwner = owners.get(key);
|
|
219
|
+
if (otherOwner) {
|
|
220
|
+
throw new ScheduleConfigError(
|
|
221
|
+
otherOwner === owner ? `${job.name} declares more than one schedule with the key \`${scheduleKey}\`` : `Both ${otherOwner} and ${owner} declare a schedule with the key \`${scheduleKey}\`. Schedules are identified by the job's name and the schedule's key, so jobs with schedules need unique names`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
owners.set(key, owner);
|
|
225
|
+
try {
|
|
226
|
+
nextCronRunAt(schedule.cron);
|
|
227
|
+
} catch (e) {
|
|
228
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
229
|
+
throw new ScheduleConfigError(
|
|
230
|
+
`The \`${scheduleKey}\` schedule of ${job.name} has an invalid cron expression \`${schedule.cron}\`: ${message}`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
const payload = scheduler.buildPayload({
|
|
234
|
+
job,
|
|
235
|
+
args: schedule.args ?? [],
|
|
236
|
+
options: { cron: schedule.cron }
|
|
237
|
+
});
|
|
238
|
+
definitions.push({ ...payload, cron: schedule.cron, key });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return definitions;
|
|
242
|
+
}
|
|
34
243
|
createWorker({ index, workoff, clear, processName }) {
|
|
35
244
|
const config = this.workers[index];
|
|
36
245
|
const adapter = this.adapters[config.adapter];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Scheduler.d.ts","sourceRoot":"","sources":["../../src/core/Scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EAChB,MAAM,wCAAwC,CAAA;
|
|
1
|
+
{"version":3,"file":"Scheduler.d.ts","sourceRoot":"","sources":["../../src/core/Scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EAChB,MAAM,wCAAwC,CAAA;AAgB/C,OAAO,KAAK,EACV,WAAW,EACX,GAAG,EACH,UAAU,EACV,kBAAkB,EACnB,MAAM,aAAa,CAAA;AAGpB,UAAU,eAAe,CAAC,QAAQ,SAAS,WAAW;IACpD,OAAO,EAAE,QAAQ,CAAA;IACjB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB;AAED,qBAAa,SAAS,CAAC,QAAQ,SAAS,WAAW;IACjD,OAAO,EAAE,QAAQ,CAAA;IACjB,MAAM,EAAE,WAAW,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;gBAE5C,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,eAAe,CAAC,QAAQ,CAAC;IAS1D,YAAY,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,IAAI,GAAG,IAAI,CAAA;KAAE;IAU1E,YAAY,CAAC,IAAI,SAAS,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC,EAAE,EACpD,GAAG,EACH,IAAI,EACJ,OAAO,GACR,EAAE;QACD,GAAG,EAAE,IAAI,CAAA;QACT,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,EAAE,CAAA;QAC3C,OAAO,CAAC,EAAE,kBAAkB,CAAA;KAC7B,GAAG,eAAe;IAyCb,QAAQ,CAAC,IAAI,SAAS,GAAG,CAAC,UAAU,EAAE,OAAO,EAAE,CAAC,EAAE,EACtD,GAAG,EACH,IAAI,EACJ,OAAO,GACR,EAAE;QACD,GAAG,EAAE,IAAI,CAAA;QACT,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,EAAE,CAAA;QAC3C,OAAO,CAAC,EAAE,kBAAkB,CAAA;KAC7B,GAAG,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAmCtD;;;;;;;;OAQG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;CASpC"}
|
package/dist/core/Scheduler.js
CHANGED
|
@@ -2,13 +2,16 @@ import {
|
|
|
2
2
|
DEFAULT_LOGGER,
|
|
3
3
|
DEFAULT_PRIORITY,
|
|
4
4
|
DEFAULT_WAIT,
|
|
5
|
-
DEFAULT_WAIT_UNTIL
|
|
5
|
+
DEFAULT_WAIT_UNTIL,
|
|
6
|
+
RESERVED_KEY_PREFIX
|
|
6
7
|
} from "../consts.js";
|
|
7
8
|
import {
|
|
8
9
|
AdapterNotConfiguredError,
|
|
9
10
|
CancelNotImplementedError,
|
|
10
11
|
QueueNotDefinedError,
|
|
11
|
-
|
|
12
|
+
ReservedKeyError,
|
|
13
|
+
SchedulingError,
|
|
14
|
+
UniqueKeysNotSupportedError
|
|
12
15
|
} from "../errors.js";
|
|
13
16
|
import { nextCronRunAt } from "../util.js";
|
|
14
17
|
class Scheduler {
|
|
@@ -40,6 +43,7 @@ class Scheduler {
|
|
|
40
43
|
const wait = options?.wait ?? DEFAULT_WAIT;
|
|
41
44
|
const waitUntil = options?.waitUntil ?? DEFAULT_WAIT_UNTIL;
|
|
42
45
|
const cron = options?.cron;
|
|
46
|
+
const key = options?.key;
|
|
43
47
|
if (!queue) {
|
|
44
48
|
throw new QueueNotDefinedError();
|
|
45
49
|
}
|
|
@@ -48,6 +52,9 @@ class Scheduler {
|
|
|
48
52
|
"Cannot schedule a cron job with wait or waitUntil options"
|
|
49
53
|
);
|
|
50
54
|
}
|
|
55
|
+
if (key?.toLowerCase().startsWith(RESERVED_KEY_PREFIX)) {
|
|
56
|
+
throw new ReservedKeyError(key, RESERVED_KEY_PREFIX);
|
|
57
|
+
}
|
|
51
58
|
return {
|
|
52
59
|
name: job.name,
|
|
53
60
|
path: job.path,
|
|
@@ -57,7 +64,10 @@ class Scheduler {
|
|
|
57
64
|
// matches, just like all following runs
|
|
58
65
|
runAt: cron ? nextCronRunAt(cron) : this.computeRunAt({ wait, waitUntil }),
|
|
59
66
|
queue,
|
|
60
|
-
priority
|
|
67
|
+
priority,
|
|
68
|
+
// Only set when given, so adapters without unique key support never
|
|
69
|
+
// see the property
|
|
70
|
+
...key !== void 0 ? { key } : {}
|
|
61
71
|
};
|
|
62
72
|
}
|
|
63
73
|
async schedule({
|
|
@@ -70,6 +80,9 @@ class Scheduler {
|
|
|
70
80
|
args,
|
|
71
81
|
options
|
|
72
82
|
});
|
|
83
|
+
if (payload.key !== void 0 && !this.adapter.supportsUniqueKeys) {
|
|
84
|
+
throw new UniqueKeysNotSupportedError();
|
|
85
|
+
}
|
|
73
86
|
this.logger.info(payload, `[CedarJS Jobs] Scheduling ${job.name}`);
|
|
74
87
|
try {
|
|
75
88
|
const scheduled = this.adapter.schedule(payload);
|
package/dist/core/Worker.d.ts
CHANGED
|
@@ -43,6 +43,14 @@ export declare class Worker {
|
|
|
43
43
|
lastCheckTime: Date;
|
|
44
44
|
constructor(options: WorkerOptions);
|
|
45
45
|
run(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Logs a warning listing the paused jobs in this worker's queues, so a
|
|
48
|
+
* pause that was forgotten shows up whenever a long-running worker starts.
|
|
49
|
+
* Does nothing when the adapter can't list jobs or there are no paused
|
|
50
|
+
* ones, and never throws: a failed query is logged and the worker starts
|
|
51
|
+
* anyway
|
|
52
|
+
*/
|
|
53
|
+
logPausedJobs(): Promise<void>;
|
|
46
54
|
get queueNames(): string;
|
|
47
55
|
}
|
|
48
56
|
export {};
|