Introduction
Cron jobs are essential when you need to execute scheduled tasks in your application. But what happens when you have multiple instances running? ๐คฏ Without proper handling, each instance might execute the job simultaneously, causing duplicate processing and potential data inconsistencies.
A solid solution is using Bull (a Node.js queue library based on Redis) to ensure that only one instance executes the cron job at a time. In this article, we'll explore how to achieve this using NestJS and Bull.
๐ Setting Up Bull in NestJS
1๏ธโฃ Install Dependencies
First, install Bull and Redis client:
npm install --save @nestjs/bull bull ioredis
2๏ธโฃ Configure BullModule
In your app.module.ts
, configure Bull to use Redis:
import { Module } from '@nestjs/common';
import { BullModule } from '@nestjs/bull';
import { MyCronJobProcessor } from './cron.processor';
import { MyCronJobService } from './cron.service';
@Module({
imports: [
BullModule.forRoot({
redis: {
host: 'localhost', // Use the Redis host
port: 6379, // Default Redis port
},
}),
BullModule.registerQueue({
name: 'cronQueue',
}),
],
providers: [MyCronJobProcessor, MyCronJobService],
})
export class AppModule {}
Note: Ensure Redis is running locally or use a cloud-hosted Redis service.
๐ฏ Implementing the Cron Job
3๏ธโฃ Creating the Cron Job Service
Weโll create a service that adds jobs to the queue at scheduled intervals.
import { Injectable } from '@nestjs/common';
import { InjectQueue } from '@nestjs/bull';
import { Queue } from 'bull';
import { Cron } from '@nestjs/schedule';
@Injectable()
export class MyCronJobService {
constructor(@InjectQueue('cronQueue') private cronQueue: Queue) {}
@Cron('*/5 * * * * *') // Runs every 5 seconds
async scheduleJob() {
await this.cronQueue.add('processData', {});
console.log('Cron job added to the queue โ
');
}
}
The @Cron
decorator schedules the job at a fixed interval, ensuring that the task is queued rather than executed by every instance.
4๏ธโฃ Processing the Cron Job
Only one instance should process the job at a time. Bull takes care of that for us! ๐
import { Process, Processor } from '@nestjs/bull';
import { Job } from 'bull';
@Processor('cronQueue')
export class MyCronJobProcessor {
@Process('processData')
async handleCronJob(job: Job) {
console.log('Processing cron job... ๐ค', job.id);
// Your task logic here (e.g., database cleanup, report generation)
}
}
๐ฆ Create a Docker compose
If you run multiple instances of your NestJS app, only one instance will process the queued job, thanks to Redis locking mechanisms in Bull.
version: '3.8'
services:
redis:
image: redis:6.2
restart: always
ports:
- '6379:6379'
networks:
- app-network
worker:
image: node:18
working_dir: /app
volumes:
- .:/app
command: ["npm", "run", "start"]
environment:
- NODE_ENV=production
- REDIS_HOST=redis
networks:
- app-network
deploy:
replicas: 3 # Example: Run 3 instances of the worker
restart_policy:
condition: on-failure
networks:
app-network:
driver: bridge
Run:
docker-compose up -d
Now, your Redis instance is ready to use! ๐
โ Conclusion
Using Bull with Redis in NestJS, weโve ensured that:
โ๏ธ Cron jobs are scheduled only once per interval
โ๏ธ Multiple instances donโt trigger duplicate executions
โ๏ธ Scalability is achieved with a queue-based approach
Now youโre ready to handle scheduled tasks like a pro! ๐ช๐ฅ
Happy coding! ๐
Top comments (0)