2022-06-24 23:28:49 +08:00
|
|
|
import Plugin from '..';
|
2023-04-25 13:12:14 +08:00
|
|
|
import { EXECUTION_STATUS, JOB_STATUS } from '../constants';
|
2022-06-24 23:28:49 +08:00
|
|
|
import ExecutionModel from '../models/Execution';
|
|
|
|
import JobModel from '../models/Job';
|
|
|
|
import Processor from '../Processor';
|
2022-07-27 18:23:16 +08:00
|
|
|
import { Instruction } from '.';
|
2022-06-24 23:28:49 +08:00
|
|
|
|
|
|
|
type ValueOf<T> = T[keyof T];
|
|
|
|
|
|
|
|
interface DelayConfig {
|
|
|
|
endStatus: ValueOf<typeof JOB_STATUS>;
|
|
|
|
duration: number;
|
|
|
|
}
|
|
|
|
|
2022-07-27 18:23:16 +08:00
|
|
|
export default class implements Instruction {
|
2022-06-24 23:28:49 +08:00
|
|
|
timers: Map<number, NodeJS.Timeout> = new Map();
|
|
|
|
|
|
|
|
constructor(protected plugin: Plugin) {
|
|
|
|
plugin.app.on('beforeStart', () => this.load());
|
2022-07-27 18:23:16 +08:00
|
|
|
plugin.app.on('beforeStop', () => this.unload());
|
2022-06-24 23:28:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
async load() {
|
|
|
|
const { model } = this.plugin.db.getCollection('jobs');
|
2023-04-25 13:12:14 +08:00
|
|
|
const jobs = (await model.findAll({
|
2022-06-24 23:28:49 +08:00
|
|
|
where: {
|
2023-04-25 13:12:14 +08:00
|
|
|
status: JOB_STATUS.PENDING,
|
2022-06-24 23:28:49 +08:00
|
|
|
},
|
|
|
|
include: [
|
|
|
|
{
|
2022-06-28 17:00:19 +08:00
|
|
|
association: 'execution',
|
|
|
|
attributes: [],
|
|
|
|
where: {
|
2023-04-25 13:12:14 +08:00
|
|
|
status: EXECUTION_STATUS.STARTED,
|
2022-06-28 17:00:19 +08:00
|
|
|
},
|
2023-04-25 13:12:14 +08:00
|
|
|
required: true,
|
2022-06-24 23:28:49 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
association: 'node',
|
2022-06-28 17:00:19 +08:00
|
|
|
attributes: ['config'],
|
2022-06-24 23:28:49 +08:00
|
|
|
where: {
|
2023-04-25 13:12:14 +08:00
|
|
|
type: 'delay',
|
2022-06-24 23:28:49 +08:00
|
|
|
},
|
2023-04-25 13:12:14 +08:00
|
|
|
required: true,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})) as JobModel[];
|
2022-06-24 23:28:49 +08:00
|
|
|
|
2023-04-25 13:12:14 +08:00
|
|
|
jobs.forEach((job) => {
|
2022-11-14 20:05:01 +08:00
|
|
|
this.schedule(job, job.node!.config.duration);
|
2022-06-24 23:28:49 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
unload() {
|
|
|
|
for (const timer of this.timers.values()) {
|
|
|
|
clearTimeout(timer);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.timers = new Map();
|
|
|
|
}
|
|
|
|
|
|
|
|
schedule(job, duration: number) {
|
|
|
|
const now = new Date();
|
|
|
|
const createdAt = Date.parse(job.createdAt);
|
|
|
|
const delay = createdAt + duration - now.getTime();
|
|
|
|
const trigger = this.trigger.bind(this, job);
|
|
|
|
this.timers.set(job.id, setTimeout(trigger, Math.max(0, delay)));
|
|
|
|
}
|
|
|
|
|
|
|
|
async trigger(job) {
|
2023-04-25 13:12:14 +08:00
|
|
|
const execution = (await job.getExecution()) as ExecutionModel;
|
2022-06-28 17:00:19 +08:00
|
|
|
if (execution.status === EXECUTION_STATUS.STARTED) {
|
2022-11-14 20:05:01 +08:00
|
|
|
job.execution = execution;
|
|
|
|
await this.plugin.resume(job);
|
2022-06-28 17:00:19 +08:00
|
|
|
}
|
2022-06-24 23:28:49 +08:00
|
|
|
if (this.timers.get(job.id)) {
|
|
|
|
this.timers.delete(job.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
run = async (node, prevJob, processor: Processor) => {
|
|
|
|
const job = await processor.saveJob({
|
|
|
|
status: JOB_STATUS.PENDING,
|
|
|
|
result: null,
|
|
|
|
nodeId: node.id,
|
2023-04-25 13:12:14 +08:00
|
|
|
upstreamId: prevJob?.id ?? null,
|
2022-06-24 23:28:49 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
const { duration } = node.config as DelayConfig;
|
|
|
|
// add to schedule
|
|
|
|
this.schedule(job, duration);
|
|
|
|
|
|
|
|
return processor.end(node, job);
|
|
|
|
};
|
|
|
|
|
|
|
|
resume = async (node, prevJob, processor: Processor) => {
|
|
|
|
const { endStatus } = node.config as DelayConfig;
|
|
|
|
prevJob.set('status', endStatus);
|
|
|
|
return prevJob;
|
|
|
|
};
|
|
|
|
}
|