import { inject, injectable } from 'inversify'; import { LoggerService } from './index.js'; import { SystemEventService } from './system-event.service.js'; import { CronJob } from 'cron'; import { MutexService } from './mutex.service.js'; import { RedisLockName } from '../models/redis.model.js'; @injectable() export class TaskSchedulerService { protected roomGarbageCollectorJob: CronJob | null = null; constructor( @inject(LoggerService) protected logger: LoggerService, @inject(SystemEventService) protected systemEventService: SystemEventService, @inject(MutexService) protected mutexService: MutexService ) {} /** * Starts the room garbage collector which runs a specified callback function every hour. * The garbage collector acquires a lock to ensure that only one instance runs at a time. * If a lock cannot be acquired, the garbage collection is skipped for that hour. * * @param callbackFn - The callback function to be executed for garbage collection. * @returns A promise that resolves when the garbage collector has been successfully started. */ async startRoomGarbageCollector(callbackFn: () => Promise): Promise { const lockTtl = 59 * 60 * 1000; // TTL of 59 minutes if (this.roomGarbageCollectorJob) { this.roomGarbageCollectorJob.stop(); this.roomGarbageCollectorJob = null; } // Create a cron job to run every hour this.roomGarbageCollectorJob = new CronJob('0 * * * *', async () => { try { const lock = await this.mutexService.acquire(RedisLockName.GARBAGE_COLLECTOR, lockTtl); if (!lock) { this.logger.debug('Failed to acquire lock for room garbage collection. Skipping.'); return; } this.logger.debug('Lock acquired for room garbage collection.'); await callbackFn(); } catch (error) { this.logger.error('Error running room garbage collection:', error); } }); // Start the job this.logger.debug('Starting room garbage collector'); this.roomGarbageCollectorJob.start(); } }