DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan Now×
Skip to content
World desk4 min

Node.js Queues for Event Gallery Batch Processing: Status, Progress, and Cancellation with BullMQ

How to run a batch such as an event gallery as a BullMQ job, report progress, expose status by job ID, stream events across workers, and cancel safely.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

With BullMQ you can run a gallery-sized batch (resizing, watermarking or zipping hundreds of event photos) as a queued job. A worker processes it asynchronously and reports progress with job.updateProgress. Clients get a stable job ID to look up current state, and a QueueEvents listener streams live changes across all workers. Cancellation works through an AbortSignal that your code must actually honor. The gallery scenario below is an illustration. The behavior described comes from BullMQ’s documentation on Workers, Events, Cancelling Jobs and the Job API.

How the pieces fit together

  • Queue and job: your API adds a job (for example, “process gallery 4821”) and returns its ID immediately instead of making the HTTP request wait.
  • Worker: runs an asynchronous processor. If the processor finishes, BullMQ moves the job to completed. If it throws, the job moves to failed, and may be retried if you configured attempts.
  • Progress: the processor calls job.updateProgress with a number or a JSON-serializable object.
  • QueueEvents: a Redis-stream-based listener that sees lifecycle and progress events from every worker.
  • Status lookup: an API endpoint keyed by job ID that reads the job’s current state on demand.

Step 1: Model a batch as one job with structured progress

Create a job for a bounded unit of work. If one job covers a whole gallery, define a progress object clients can rely on, with stable fields: completed count, total count and a short phase label. Keep internal data (file paths, storage credentials, stack details) out of it, because this object will likely reach browsers.

As an Amazon Associate I earn from qualifying purchases.

import { Queue, Worker } from 'bullmq';

const connection = { host: '127.0.0.1', port: 6379 };
export const galleryQueue = new Queue('gallery-processing', { connection });

// API handler
const job = await galleryQueue.add('process-gallery', { galleryId: 4821, photoIds });
// respond with { jobId: job.id }

// Worker
const worker = new Worker('gallery-processing', async (job, token, signal) => {
  const total = job.data.photoIds.length;
  for (let i = 0; i < total; i++) {
    signal?.throwIfAborted();
    await processPhoto(job.data.photoIds[i], { signal });
    await job.updateProgress({ completed: i + 1, total, phase: 'resizing' });
  }
  return { processed: total };
}, { connection });

The processor receives an optional AbortSignal, hence the signal?. check. Whether it is supplied depends on your BullMQ version, so confirm against the version you have installed.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Step 2: Give callers a status endpoint

Return a status resource keyed by the job ID and query current queue state when a client asks. A typical handler fetches the job, reads its state, and returns the state, the latest progress value and, once finished, the return value or failure reason.

const job = await galleryQueue.getJob(req.params.id);
if (!job) return res.sendStatus(404);
res.json({
  state: await job.getState(),
  progress: job.progress,
  result: job.returnvalue,
  failedReason: job.failedReason
});

Live events complement this lookup; they do not replace it.

Step 3: Push live updates with QueueEvents

Listeners attached to a Worker are local to the worker that handled the job. If your API process or dashboard service is separate, or you run several workers, use QueueEvents, which BullMQ documents as the cross-worker option.

import { QueueEvents } from 'bullmq';

const queueEvents = new QueueEvents('gallery-processing', { connection });

queueEvents.on('progress', ({ jobId, data }) => {
  broadcast(jobId, { type: 'progress', data });   // WebSocket or SSE
});
queueEvents.on('completed', ({ jobId, returnvalue }) => broadcast(jobId, { type: 'completed' }));
queueEvents.on('failed', ({ jobId, failedReason }) => broadcast(jobId, { type: 'failed' }));

If a request must simply block until one job ends, the Job API offers job.waitUntilFinished(queueEvents). For long galleries, prefer returning the ID and letting the client poll or subscribe.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Events are not an audit log

BullMQ documents QueueEvents as Redis-stream based, with the stream automatically trimmed to approximately 10,000 events by default (the maximum is configurable). Two consequences follow:

  • A client that reconnects should fetch current state from the status endpoint, not assume every past event is still available.
  • Persist business-critical history (who published which gallery, when) in your own database.

Step 4: Cancel work that is running

Cancellation is cooperative. The worker hands the processor an optional AbortSignal, but nothing stops unless your processor and the operations it starts respond to that signal. Calling a cancel method on the worker for an active job (recent BullMQ versions expose these; see the Cancelling Jobs page for the exact methods in yours) only triggers the signal.

  1. Check signal.aborted or call signal.throwIfAborted() at safe points, such as between photos.
  2. Pass the signal to APIs that accept one (fetch, many HTTP and file-streaming libraries).
  3. For custom operations, add an abort listener that really stops the work, for example destroying a stream or killing a child process.
  4. Release files, sockets and database clients before rejecting.
function resizeWithAbort(photo, signal) {
  return new Promise((resolve, reject) => {
    const task = startResize(photo);
    const onAbort = () => { task.cancel(); cleanup(task); reject(signal.reason); };
    signal.addEventListener('abort', onAbort, { once: true });
    task.done.then(resolve, reject)
      .finally(() => signal.removeEventListener('abort', onAbort));
  });
}

Decide whether cancellation should retry

The error you throw matters. In BullMQ’s documented pattern, a normal error can be retried if attempts remain, while throwing UnrecoverableError prevents a retry. For a user pressing “Cancel”, you almost always want the terminal behavior:

import { UnrecoverableError } from 'bullmq';

if (signal?.aborted) {
  await cleanup();
  throw new UnrecoverableError('Cancelled by user');
}

Then reflect this in your API: expose a distinct “cancelled” status to clients, since BullMQ itself will record the job as failed.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Failure modes to plan for

Problem Cause What to do
Cancel accepted, work continues Abort signal is not linked to a real cancellation mechanism Check the signal in loops and attach abort handlers to custom operations
Cancelled job runs again A regular error was thrown while attempts remained Throw UnrecoverableError for terminal cancellation
Dashboard misses completions Listening to local worker events only Use QueueEvents
Reconnecting client has gaps Event stream is trimmed (about 10,000 events by default) Refetch state by job ID on reconnect
Leaked handles after cancel Cleanup skipped on the abort path Close files, sockets and clients before rejecting
Redis connection lingers on deploy QueueEvents not closed Call await queueEvents.close() during shutdown

What this does not establish

BullMQ’s documentation does not give deployment-specific throughput figures, application-level exactly-once guarantees, or comparisons with other queue libraries, so none are claimed here. If you evaluate alternatives, compare them on: backend dependency, how job state is queried, local versus global events, progress shape and persistence, cancellation propagation and cleanup duties, retry semantics on cancellation, and retention of history.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.