adonisjs-jobs
Version:
Job processing for AdonisJS
103 lines (102 loc) • 4.34 kB
JavaScript
import { Worker as BullWorker } from 'bullmq';
import * as devalue from 'devalue';
import { REVIVERS } from './devalue.js';
export class Worker {
app;
config;
workers = [];
constructor(app, config = { queues: [], concurrency: 1 }) {
this.app = app;
this.config = config;
}
async start() {
const config = this.app.config.get('jobs', {});
const logger = await this.app.container.make('logger');
const jobs = await this.app.container.make('jobs.list');
const queues = this.config.queues && this.config.queues.length ? this.config.queues : config.queue.split(',');
const workers = [];
const revivers = {
...REVIVERS,
...(config.serialization?.revivers || {}),
};
this.app.terminating(async () => {
await Promise.allSettled(workers.map((worker) => worker.close()));
});
for (const [index, queueName] of queues.entries()) {
let concurrency = Array.isArray(this.config.concurrency)
? (this.config.concurrency[index] ?? this.config.concurrency[0] ?? 1)
: (this.config.concurrency ?? 1);
const worker = new BullWorker(queueName, async (job) => {
const jobClass = jobs[job.name];
if (!jobClass) {
logger.error(`Cannot find job ${job.name}`);
}
let instance;
try {
instance = await this.app.container.make(jobClass);
}
catch (error) {
logger.error(`Cannot instantiate job ${job.name}`);
return;
}
instance.job = job;
instance.logger = logger;
instance.payload = devalue.parse(job.data, revivers);
logger.info({ id: job.id, options: job.opts, payload: instance.payload }, `Job ${job.name} (${job.id}) started`);
let result = await instance.handle(instance.payload);
logger.info({ id: job.id, options: job.opts, payload: instance.payload }, `Job ${job.name} (${job.id}) finished`);
return result;
}, {
...(config.workerOptions || {}),
connection: config.connection,
concurrency,
});
worker.on('failed', async (job, error) => {
logger.error(error.message, []);
if (job && (job.attemptsMade === job.opts.attempts || job.finishedOn)) {
const jobClass = jobs[job.name];
if (!jobClass) {
logger.error(`Cannot find job ${job.name}`);
}
let instance;
try {
instance = await this.app.container.make(jobClass);
}
catch {
logger.error(`Cannot instantiate job ${job.name}`);
return;
}
instance.job = job;
instance.logger = logger;
instance.payload = devalue.parse(job.data, revivers);
await instance.failed?.(error);
}
});
worker.on('completed', async (job, result) => {
if (!job)
return;
const jobClass = jobs[job.name];
if (!jobClass) {
logger.error(`Cannot find job ${job.name}`);
}
let instance;
try {
instance = await this.app.container.make(jobClass);
}
catch {
logger.error(`Cannot instantiate job ${job.name}`);
return;
}
instance.job = job;
instance.logger = logger;
instance.payload = devalue.parse(job.data, revivers);
await instance.completed?.(instance.payload, result);
});
this.workers.push(worker);
}
logger.info(`Processing jobs from the ${JSON.stringify(queues)} queues.`);
}
async stop() {
await Promise.allSettled(this.workers.map((w) => w.close()));
}
}