import { Logger } from 'pino'; type FunctionNoArgs = () => Promise; /** * Run a job (function) in some interval, * but make sure we run exactly ONE job at the same time */ export class SinglePeriodicJobRunner { private interval: NodeJS.Timeout; private isWorking: boolean = false; private logger: Logger; constructor( private name: string, private intervalMs: number, logger: Logger, private warningOverlap: boolean = true, private warningOverride: boolean = false, ) { this.logger = logger.child({ job: name, class: SinglePeriodicJobRunner.name, }); } start(fn: FunctionNoArgs): boolean { if (this.interval) { const msg = `Job has been started before, do not schedule it again`; this.log(this.warningOverride, msg); return false; } this.interval = setInterval(() => { if (this.isWorking) { const msg = `Job is already running, skipping this run`; this.log(this.warningOverlap, msg); return; } this.isWorking = true; this.logger.debug('Running job...'); fn() .catch((error) => { this.logger.error(`Job failed: ${error}`); this.logger.error(error.stack); }) .finally(() => { this.isWorking = false; this.logger.debug(`Job finished`); }); }, this.intervalMs); this.logger.info(`Job started with interval ${this.intervalMs} ms`); return true; } stop() { if (!this.interval) { return; } clearInterval(this.interval); this.interval = null; this.logger.info(`Job stopped`); } private log(warning: boolean, msg) { if (warning) { this.logger.warn(msg); } else { this.logger.info(msg); } } }