@clairejs/server 3.23.3 → 3.23.5
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 +3 -1
- package/dist/job/AbstractJobScheduler.d.ts +2 -1
- package/dist/job/AbstractJobScheduler.js +48 -1
- package/dist/job/AwsJobScheduler.d.ts +1 -2
- package/dist/job/AwsJobScheduler.js +1 -34
- package/dist/job/LocalJobScheduler.d.ts +1 -0
- package/dist/job/LocalJobScheduler.js +4 -17
- package/dist/job/job.js +6 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,10 +13,11 @@ export declare abstract class AbstractJobScheduler {
|
|
|
13
13
|
private _jobs;
|
|
14
14
|
constructor(logger: AbstractLogger, db: ITransactionFactory, jobRepo: AbstractJobRepository);
|
|
15
15
|
protected getCurrentJobHandlers(): Promise<JobHandlerMetadata[]>;
|
|
16
|
+
protected abstract getScheduledJobs(): Promise<AbstractJob[]>;
|
|
16
17
|
/**
|
|
17
18
|
* Sync all jobs to running state. This should be called only at init time.
|
|
18
19
|
*/
|
|
19
|
-
|
|
20
|
+
syncJobs(): Promise<void>;
|
|
20
21
|
/**
|
|
21
22
|
* Return unique job id which can then be used to cancel the job
|
|
22
23
|
* @param payload the necessary info to launch the job
|
|
@@ -22,6 +22,51 @@ export class AbstractJobScheduler {
|
|
|
22
22
|
}
|
|
23
23
|
return this._jobs;
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Sync all jobs to running state. This should be called only at init time.
|
|
27
|
+
*/
|
|
28
|
+
async syncJobs() {
|
|
29
|
+
//-- check jobs
|
|
30
|
+
const scheduledJobs = await this.getScheduledJobs();
|
|
31
|
+
const allHandlers = await this.getCurrentJobHandlers();
|
|
32
|
+
const tx = await this.db.createTransaction();
|
|
33
|
+
//-- remove job that no more exist
|
|
34
|
+
const nomoreExistJobs = scheduledJobs.filter((job) => !allHandlers.find((handler) => handler.jobName === job.jobName));
|
|
35
|
+
for (const job of nomoreExistJobs) {
|
|
36
|
+
this.logger.info(`Removing stale job: ${job.jobName} of id: ${job.id}`);
|
|
37
|
+
await this.disableJob(job.id, tx);
|
|
38
|
+
}
|
|
39
|
+
if (nomoreExistJobs.length) {
|
|
40
|
+
this.logger.info(`Cleaned up: ${nomoreExistJobs.length} stale jobs`);
|
|
41
|
+
}
|
|
42
|
+
//-- remove scheduled cron jobs that diff the cron expression
|
|
43
|
+
this.logger.debug("Remove scheduled cron jobs");
|
|
44
|
+
const scheduledCronJobs = scheduledJobs.filter((j) => j.cron);
|
|
45
|
+
const updatedScheduledCronJobs = scheduledCronJobs.filter((j) => j.cron && allHandlers.some((job) => job.jobName === j.jobName && job.cron !== j.cron));
|
|
46
|
+
for (const job of updatedScheduledCronJobs) {
|
|
47
|
+
await this.disableJob(job.id, tx);
|
|
48
|
+
}
|
|
49
|
+
await tx.commit();
|
|
50
|
+
//-- reschedule new cron jobs and those which are not synced
|
|
51
|
+
const resyncCronJobs = allHandlers.filter((job) => job.cron &&
|
|
52
|
+
(updatedScheduledCronJobs.some((j) => j.jobName === job.jobName) ||
|
|
53
|
+
!scheduledCronJobs.some((j) => j.jobName === job.jobName)));
|
|
54
|
+
this.logger.debug(`Resync ${resyncCronJobs.length} cron jobs`, resyncCronJobs);
|
|
55
|
+
for (const job of resyncCronJobs) {
|
|
56
|
+
await this.scheduleJob({
|
|
57
|
+
...job.retryOptions,
|
|
58
|
+
jobName: job.jobName,
|
|
59
|
+
cron: job.cron,
|
|
60
|
+
at: job.at,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//-- sync "at" jobs
|
|
64
|
+
const allPersistedAtJobs = await this.jobRepo.getJobs({ _neq: { disabled: true, at: undefined } });
|
|
65
|
+
const missingScheduledAtJobs = allPersistedAtJobs.filter((job) => !scheduledJobs.find((scheduled) => scheduled.id === job.id));
|
|
66
|
+
for (const job of missingScheduledAtJobs) {
|
|
67
|
+
await this.scheduleJob(job);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
25
70
|
async scheduleJob({ id, ...payload }) {
|
|
26
71
|
let jobId = id;
|
|
27
72
|
const tx = await this.db.createTransaction();
|
|
@@ -31,7 +76,9 @@ export class AbstractJobScheduler {
|
|
|
31
76
|
else {
|
|
32
77
|
const job = jobId
|
|
33
78
|
? await this.jobRepo.getJobById(jobId)
|
|
34
|
-
: await this.jobRepo
|
|
79
|
+
: await this.jobRepo
|
|
80
|
+
.getJobs({ _eq: { jobName: payload.jobName } }, { limit: 1 })
|
|
81
|
+
.then((jobs) => jobs[0]);
|
|
35
82
|
if (!job) {
|
|
36
83
|
jobId = await this.jobRepo.insertJob(payload, tx);
|
|
37
84
|
}
|
|
@@ -19,8 +19,7 @@ export declare class AwsJobScheduler extends AbstractJobScheduler {
|
|
|
19
19
|
constructor(logger: AbstractLogger, db: AbstractDbAdapter, jobRepo: AbstractJobRepository, apiLambdaFunctionArn: string, jobNamespace: string, eventBusName?: string);
|
|
20
20
|
handleCron(jobInfo: AbstractJob): Promise<void>;
|
|
21
21
|
private generateCronFromTimestamp;
|
|
22
|
-
|
|
22
|
+
protected getScheduledJobs(): Promise<AbstractJob[]>;
|
|
23
23
|
protected _scheduleJob(jobInfo: AbstractJob): Promise<void>;
|
|
24
|
-
syncJobs(): Promise<void>;
|
|
25
24
|
cancelJob(jobId: string): Promise<void>;
|
|
26
25
|
}
|
|
@@ -49,7 +49,7 @@ let AwsJobScheduler = class AwsJobScheduler extends AbstractJobScheduler {
|
|
|
49
49
|
date.getUTCFullYear(),
|
|
50
50
|
].join(" ");
|
|
51
51
|
}
|
|
52
|
-
async
|
|
52
|
+
async getScheduledJobs() {
|
|
53
53
|
const allRules = await this.eventbridge
|
|
54
54
|
.listRules({
|
|
55
55
|
EventBusName: this.eventBusName,
|
|
@@ -125,39 +125,6 @@ let AwsJobScheduler = class AwsJobScheduler extends AbstractJobScheduler {
|
|
|
125
125
|
throw Errors.SYSTEM_ERROR(`Job does not have time config: ${jobInfo.jobName}`);
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
|
-
async syncJobs() {
|
|
129
|
-
//-- check jobs
|
|
130
|
-
const scheduledJobs = await this.getAllScheduledJobs();
|
|
131
|
-
const allHandlers = await this.getCurrentJobHandlers();
|
|
132
|
-
//-- remove job that no more exist
|
|
133
|
-
const nomoreExistJobs = scheduledJobs.filter((job) => !allHandlers.find((handler) => handler.jobName === job.jobName));
|
|
134
|
-
for (const job of nomoreExistJobs) {
|
|
135
|
-
this.logger.info(`Removing stale job: ${job.jobName} of id: ${job.id}`);
|
|
136
|
-
await this.disableJob(job.id);
|
|
137
|
-
}
|
|
138
|
-
if (nomoreExistJobs.length) {
|
|
139
|
-
this.logger.info(`Cleaned up: ${nomoreExistJobs.length} stale jobs`);
|
|
140
|
-
}
|
|
141
|
-
//-- remove scheduled cron jobs that diff the cron expression
|
|
142
|
-
this.logger.debug("Remove scheduled cron jobs");
|
|
143
|
-
const scheduledCronJobs = scheduledJobs.filter((j) => j.cron);
|
|
144
|
-
const unmatchedCronJobs = scheduledCronJobs.filter((j) => j.cron && !allHandlers.find((job) => job.jobName === j.jobName && job.cron === j.cron));
|
|
145
|
-
if (unmatchedCronJobs.length) {
|
|
146
|
-
await Promise.all(unmatchedCronJobs.map((j) => this.disableJob(j.id)));
|
|
147
|
-
}
|
|
148
|
-
//-- reschedule new cron jobs and those which are not synced
|
|
149
|
-
const resyncCronJobs = allHandlers.filter((job) => job.cron &&
|
|
150
|
-
(unmatchedCronJobs.some((j) => j.jobName === job.jobName) ||
|
|
151
|
-
!scheduledCronJobs.some((j) => j.jobName === job.jobName)));
|
|
152
|
-
this.logger.debug("Reschedule cron jobs", resyncCronJobs);
|
|
153
|
-
for (const job of resyncCronJobs) {
|
|
154
|
-
await this.scheduleJob({
|
|
155
|
-
jobName: job.jobName,
|
|
156
|
-
cron: job.cron,
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
//-- keep "at" jobs as is
|
|
160
|
-
}
|
|
161
128
|
async cancelJob(jobId) {
|
|
162
129
|
await this.eventbridge
|
|
163
130
|
.removeTargets({
|
|
@@ -52,6 +52,7 @@ export declare class LocalJobScheduler extends AbstractJobScheduler implements I
|
|
|
52
52
|
private extendMutexKey;
|
|
53
53
|
init(): Promise<void>;
|
|
54
54
|
exit(): void;
|
|
55
|
+
protected getScheduledJobs(): Promise<AbstractJob[]>;
|
|
55
56
|
syncJobs(): Promise<void>;
|
|
56
57
|
protected _scheduleJob(jobInfo: AbstractJob): Promise<void>;
|
|
57
58
|
protected cancelJob(jobId: string): Promise<void>;
|
|
@@ -142,25 +142,12 @@ let LocalJobScheduler = class LocalJobScheduler extends AbstractJobScheduler {
|
|
|
142
142
|
}
|
|
143
143
|
this.logger.debug("LocalJobScheduler exit");
|
|
144
144
|
}
|
|
145
|
+
async getScheduledJobs() {
|
|
146
|
+
return Object.values(this.jobHolder).flatMap((job) => (job?.jobInfo ? [job.jobInfo] : []));
|
|
147
|
+
}
|
|
145
148
|
async syncJobs() {
|
|
146
149
|
if (this.isActive) {
|
|
147
|
-
|
|
148
|
-
const allJobs = await this.getCurrentJobHandlers();
|
|
149
|
-
for (const job of allJobs) {
|
|
150
|
-
if (job.cron) {
|
|
151
|
-
await this.scheduleJob({
|
|
152
|
-
...job.retryOptions,
|
|
153
|
-
jobName: job.jobName,
|
|
154
|
-
cron: job.cron,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
//-- re-schedule "at" jobs that are stored in repo
|
|
159
|
-
const allPersistedJobs = await this.jobRepo.getJobs({ _neq: { disabled: true, at: undefined } });
|
|
160
|
-
//-- run job anyway, expired job will be removed then
|
|
161
|
-
for (const job of allPersistedJobs) {
|
|
162
|
-
await this.scheduleJob(job);
|
|
163
|
-
}
|
|
150
|
+
await super.syncJobs();
|
|
164
151
|
}
|
|
165
152
|
else {
|
|
166
153
|
this.sendJob(CommunicationMessage.SYNC_JOB);
|
package/dist/job/job.js
CHANGED
|
@@ -83,10 +83,14 @@ __decorate([
|
|
|
83
83
|
__metadata("design:type", String)
|
|
84
84
|
], AbstractJobModel.prototype, "lastError", void 0);
|
|
85
85
|
__decorate([
|
|
86
|
-
Column({
|
|
86
|
+
Column({
|
|
87
|
+
isTimestamp: true,
|
|
88
|
+
}),
|
|
87
89
|
__metadata("design:type", Number)
|
|
88
90
|
], AbstractJobModel.prototype, "lastRunAt", void 0);
|
|
89
91
|
__decorate([
|
|
90
|
-
Column({
|
|
92
|
+
Column({
|
|
93
|
+
isTimestamp: true,
|
|
94
|
+
}),
|
|
91
95
|
__metadata("design:type", Number)
|
|
92
96
|
], AbstractJobModel.prototype, "lastSuccessAt", void 0);
|