@fellas/adonisjs-resque
Version:
Resque Queue for AdonisJS v6
217 lines (214 loc) • 6.66 kB
JavaScript
import {
importAllJobs
} from "../chunk-NKW4PRQD.js";
import {
getConfig
} from "../chunk-MZLOY7HF.js";
import "../chunk-ASV6JZ7C.js";
import {
getConnection
} from "../chunk-5SDUSZZN.js";
import {
__decorateClass
} from "../chunk-EUXUH3YW.js";
// commands/resque_start.ts
import { BaseCommand, flags } from "@adonisjs/core/ace";
import { createWorker, createMultiWorker, isMultiWorkerEnabled } from "@fellas/adonisjs-resque/services/main";
// scheduler.ts
import { Scheduler } from "node-resque";
import Cron from "croner";
import ms from "ms";
function createScheduler() {
return new Scheduler({
connection: getConnection()
});
}
async function startJobSchedules(resqueScheduler, jobs) {
const intervals = [];
const isLeader = () => {
return resqueScheduler.leader;
};
const createCronerFor = (job) => {
if (job.cron) {
return Cron(job.cron, async () => {
if (isLeader()) {
return await job.enqueue();
}
});
}
};
const createRepeaterFor = (job) => {
if (!job.interval) {
return;
}
let milliseconds;
if (typeof job.interval === "number") {
milliseconds = job.interval;
} else {
milliseconds = ms(job.interval);
}
const intervalId = setInterval(async () => {
if (isLeader()) {
await job.enqueue();
}
}, milliseconds);
return intervalId;
};
for (const { job } of Object.values(jobs)) {
const croner = createCronerFor(job);
if (croner) {
intervals.push(croner);
}
const intervalId = createRepeaterFor(job);
if (intervalId) {
intervals.push(intervalId);
}
}
return intervals;
}
function cancelSchedules(intervals) {
if (!intervals) {
return;
}
for (const inteval of intervals) {
if (inteval instanceof Cron) {
inteval.stop();
} else {
clearInterval(inteval);
}
}
}
// commands/resque_start.ts
var ResqueStart = class extends BaseCommand {
static commandName = "resque:start";
static description = "Start workers / schedules for resque";
static options = {
startApp: true,
staysAlive: true
};
intervals;
workerInstance;
schedulerInstance;
async run() {
const pid = process.pid;
const jobs = await importAllJobs();
const emitter = await this.app.container.make("emitter");
const verbose = this.verbose ?? getConfig("verbose");
if (this.worker) {
const queueNames = this.queueName ?? getConfig("queueNameForWorkers").split(",").map((value) => value.trim()).filter((value) => value !== "");
const isMultiWorker = this.isMulti ?? isMultiWorkerEnabled();
if (isMultiWorker) {
this.workerInstance = createMultiWorker(jobs, queueNames);
this.logger.info(`Resque multiWorker:${pid} started with ${Object.keys(jobs).length} jobs`);
this.workerInstance.on("failure", (workerId, queue, job, failure, duration) => {
if (verbose)
this.logger.info(`Job ${job.class} in queue ${queue} failed on worker ${workerId} in ${duration}ms`);
emitter.emit("resque:failure", {
workerId,
queue,
job: jobs[job.class],
failure,
duration,
args: job.args,
pluginOptions: job.pluginOptions
});
});
this.workerInstance.on("cleaning_worker", (workerId, _worker, pid2) => {
if (verbose)
this.logger.info(`Worker ${workerId} (PID ${pid2}) cleaning up...`);
});
this.workerInstance.on("end", (workerId) => {
if (verbose)
this.logger.info(`Worker ${workerId} ended.`);
});
this.workerInstance.on("success", (workerId, queue, job, result, duration) => {
if (verbose)
this.logger.success(`Job ${job.class} in queue ${queue} completed on worker ${workerId} in ${duration}ms, result: ${JSON.stringify(result)}`);
});
} else {
this.workerInstance = createWorker(jobs, queueNames);
this.workerInstance.on("failure", (queue, job, failure, duration) => {
if (verbose)
this.logger.info(`Job ${job.class} in queue ${queue} failed in ${duration}ms`);
emitter.emit("resque:failure", {
queue,
job: jobs[job.class],
failure,
duration,
args: job.args,
pluginOptions: job.pluginOptions
});
});
this.workerInstance.on("success", (queue, job, result, duration) => {
if (verbose)
this.logger.success(`Job ${job.class} in queue ${queue} completed in ${duration}ms, result: ${JSON.stringify(result)}`);
});
await this.workerInstance.connect();
}
await this.workerInstance.start();
}
const runScheduler = getConfig("runScheduler");
if (this.schedule ?? runScheduler) {
this.schedulerInstance = createScheduler();
await this.schedulerInstance.connect();
await this.schedulerInstance.start();
this.schedulerInstance.on("end", () => {
if (verbose)
this.logger.info(`Scheduler ended.`);
});
this.intervals = await startJobSchedules(this.schedulerInstance, jobs);
if (verbose)
this.logger.info(`Scheduler:${pid} started`);
}
}
prepare() {
const isVerbose = this.verbose ?? getConfig("verbose");
const terminate = async (signal) => {
if (isVerbose)
this.logger.info("Receive " + signal);
await this.terminate();
};
const cleanup = async () => {
this.logger.info("Resque worker terminating...");
if (this.workerInstance) {
await this.workerInstance.end();
}
if (this.schedulerInstance) {
await this.schedulerInstance.end();
}
cancelSchedules(this.intervals);
};
this.app.listen("SIGINT", terminate).listen("SIGTERM", terminate);
this.app.terminating(cleanup);
}
};
__decorateClass([
flags.boolean({
description: "start job schedule"
})
], ResqueStart.prototype, "schedule", 2);
__decorateClass([
flags.boolean({
description: "start workers",
default: true
})
], ResqueStart.prototype, "worker", 2);
__decorateClass([
flags.boolean({
description: "multi workers"
})
], ResqueStart.prototype, "isMulti", 2);
__decorateClass([
flags.boolean({
description: "enable log verbose"
})
], ResqueStart.prototype, "verbose", 2);
__decorateClass([
flags.array({
description: "queue names for worker to listen"
})
], ResqueStart.prototype, "queueName", 2);
export {
ResqueStart as default
};
//# sourceMappingURL=resque_start.js.map