DEV Community

Cover image for ๐Ÿš€ Handling Cron Jobs in NestJS with Multiple Instances using Bull
Juan Castillo
Juan Castillo

Posted on

1

๐Ÿš€ Handling Cron Jobs in NestJS with Multiple Instances using Bull

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
Enter fullscreen mode Exit fullscreen mode

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 {}
Enter fullscreen mode Exit fullscreen mode

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 โœ…');
  }
}
Enter fullscreen mode Exit fullscreen mode

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)
  }
}
Enter fullscreen mode Exit fullscreen mode

๐Ÿ“ฆ 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
Enter fullscreen mode Exit fullscreen mode

Run:

docker-compose up -d
Enter fullscreen mode Exit fullscreen mode

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! ๐Ÿš€

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (0)

Image of Datadog

Create and maintain end-to-end frontend tests

Learn best practices on creating frontend tests, testing on-premise apps, integrating tests into your CI/CD pipeline, and using Datadogโ€™s testing tunnel.

Download The Guide

๐Ÿ‘‹ Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someoneโ€™s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay