Understand the difference between parallelism and concurrency in BullMQ
Understanding parallelism and concurrency is crucial for maximizing throughput and efficiently processing jobs in BullMQ. While these terms are often used interchangeably, they have distinct meanings that affect how you configure your workers.
Parallelism means that two or more tasks run simultaneously on different CPU cores or machines. True parallel execution happens when:
You have multiple CPU cores available
Multiple machines are processing jobs
Tasks are genuinely executing at the exact same time
import { Worker } from 'bullmq';import cluster from 'cluster';import os from 'os';if (cluster.isPrimary) { // Fork workers equal to CPU count const numCPUs = os.cpus().length; console.log(`Starting ${numCPUs} worker processes`); for (let i = 0; i < numCPUs; i++) { cluster.fork(); }} else { // Each process runs a worker const worker = new Worker('tasks', async job => { // CPU-intensive work runs in parallel across processes return await cpuIntensiveTask(job.data); });}
Concurrency means multiple tasks make progress by sharing CPU time through rapid context switching. Tasks give the impression of running in parallel, but they’re actually taking turns.
import { Worker } from 'bullmq';const worker = new Worker('tasks', async job => { // While waiting for API response, worker can process other jobs const response = await fetch(job.data.url); // While waiting for database write, worker can process other jobs await database.save(response.data); return response.data;}, { concurrency: 50, // Process up to 50 jobs concurrently});
For CPU-intensive jobs without I/O, keep concurrency low:
const cpuWorker = new Worker('heavy-calc', async job => { // Pure CPU work - no I/O return calculatePrimes(job.data.max);}, { concurrency: 1, // Low concurrency for CPU-bound work});
High concurrency with CPU-intensive jobs adds overhead without benefit. Use Sandboxed Processors instead for true parallelism.
Recommended concurrency for CPU-intensive jobs: 1-2
import path from 'path';const imageWorker = new Worker( 'images', path.join(__dirname, 'image-processor.js'), { concurrency: os.cpus().length, // Match CPU cores },);