UNPKG

@jose_velaz/adonisjs-scheduler

Version:
116 lines (115 loc) 4.85 kB
import cron from 'node-cron'; import AsyncLock from 'async-lock'; import { FsLoader, Kernel } from '@adonisjs/core/ace'; const lock = new AsyncLock(); const run = async (cb, options) => { if (!options.enabled) return await cb(); if (lock.isBusy(options.key)) { if (options.onBusy) { await options.onBusy(); } return; } await lock.acquire(options.key, cb, { maxPending: 1, timeout: options.timeout }); }; export class Worker { app; commandsToRun; tasks = []; loaders = []; booted = false; constructor(app, commandsToRun) { this.app = app; this.commandsToRun = commandsToRun; } async boot() { if (this.booted) return; const schedule = await this.app.container.make('scheduler'); await schedule.boot(); const fsLoader = new FsLoader(this.app.commandsPath()); this.loaders = [fsLoader]; this.app.rcFile.commands.forEach((commandModule) => { this.loaders.push(() => typeof commandModule === 'function' ? commandModule() : this.app.import(commandModule)); }); this.booted = true; } async start() { await this.boot(); const schedule = await this.app.container.make('scheduler'); const logger = await this.app.container.make('logger'); if (schedule.onStartingCallback) { await schedule.onStartingCallback(); } for (let index = 0; index < schedule.items.length; index++) { const command = schedule.items[index]; if (this.commandsToRun?.length && ((command.alias && !this.commandsToRun.includes(command.alias)) || (command.type === 'command' && !this.commandsToRun.includes(command.commandName)))) { continue; } this.tasks.push(cron.schedule(command.expression, async () => { try { switch (command.type) { case 'command': const ace = new Kernel(this.app); for (const loader of this.loaders) { ace.addLoader(loader); } for (const callback of command.beforeCallbacks) { await callback(); } await run(() => ace.exec(command.commandName, command.commandArgs), { enabled: command.config.withoutOverlapping, timeout: command.config.expiresAt, key: `${index}-${command.commandName}-${command.commandArgs}`, onBusy: () => { logger.warn(`Command ${index}-${command.commandName}-${command.commandArgs} is busy`); }, }); for (const callback of command.afterCallbacks) { await callback(); } break; case 'callback': for (const callback of command.beforeCallbacks) { await callback(); } await run(() => command.callback(), { enabled: command.config.withoutOverlapping, timeout: command.config.expiresAt, key: `${index}-callback`, onBusy: () => { logger.warn(`Callback ${index} is busy`); }, }); for (const callback of command.afterCallbacks) { await callback(); } default: break; } } catch (error) { logger.error(error); } }, { scheduled: command.config.enabled, timezone: command.config.timezone, runOnInit: command.config.enabled && command.config.immediate, })); } if (!this.tasks.length) { logger.error('Unable to start worker. No tasks found. Please verify the command name or alias'); return; } logger.info(`Schedule worker started successfully.`); if (schedule.onStartedCallback) { await schedule.onStartedCallback(); } } async stop() { await Promise.all(this.tasks.map((task) => task.stop())); } }