Retries and graceful degradation

Background jobs fail. Email providers go down, external APIs rate-limit you, and the occasional malformed payload blows up your handler. The queue has to be designed for this reality, not the happy path.

BullMQ retries failed jobs automatically based on config. You decide how many attempts, how long to wait between them, and whether to use exponential backoff. A job that fails three attempts in a row moves to the failed state, where a separate process or a human can investigate and replay it.

after/services/order.service.ts
typescript
import { db } from '../db';
import { orders, Order } from '../db/schema';
import { getOrderQueue } from '../queue';

export async function placeOrder(
  userId: number,
  bookId: number,
  quantity: number,
): Promise<Order> {
  // Step 1: durable write
  const result = await db
    .insert(orders)
    .values({ userId, bookId, quantity, status: 'pending' })
    .returning();
  const newOrder = result[0];

  // Step 2: enqueue the background job, but do not let Redis failure kill the request
  try {
    await getOrderQueue().add('sendConfirmationEmail', {
      orderId: newOrder.id,
      userId,
      bookId,
      quantity,
    });
  } catch (err) {
    console.warn('Could not enqueue order job: ' + (err as Error).message);
  }

  return newOrder;
}

Save the order first. Enqueue inside try/catch so a Redis outage does not take the API down with it.

Read the service carefully. The database write happens first because that is the source of truth. The queue dispatch is wrapped in try/catch because Redis is a convenience, not a correctness requirement. If Redis is down, the order still exists. A reconciliation job or a manual replay can pick it up later. This is what graceful degradation looks like in practice.

No. You configure a max attempts count on each job, usually three to five. After the last attempt, the job moves to the failed state and stays there. You can build a dead-letter queue that collects failed jobs for inspection, or attach an alert so an on-call engineer sees them. Either way, infinite retries are never the right answer.

Quiz: Quiz

Loading practice…