When to add a queue

Start with the PDF upload requirement, find where the request path becomes fragile, then improve the design with a queue-backed path without losing interview scope.

System design deep dive8:46 video + written guideBy Daniyal Saqib
Watch the walkthrough, then follow the written guide below.Watch on YouTube
Learning depth

Follow one uploaded PDF from acceptance to searchability. Derive the asynchronous boundary from the user’s requirement, preserve accepted work through a crash, and explain why durable waiting still needs enough processing capacity.

Complete Mid article · 7 sections.
01

Begin with the product requirement

The product may acknowledge a durable upload before the document becomes searchable.

A user uploads a PDF so that search and chat can reference it later. The product allows two visible states: the upload is accepted, then the document becomes searchable. That permission is the reason we can consider background processing. A slow operation alone would not establish it. If the user needed the complete searchable result in this response, we would have a different request contract.

Hand-drawn Excalidraw diagram showing a client uploading a PDF, the upload service storing it in object storage, fast acknowledgement, later document processing, and a searchable documentHand-drawn Excalidraw diagram showing a client uploading a PDF, the upload service storing it in object storage, fast acknowledgement, later document processing, and a searchable document
The requirement. Original video diagram. Open the image at full size; scroll horizontally on smaller screens.

The product needs fast acknowledgement of a durable upload. Making the PDF searchable can finish afterward.

Follow document doc-456 and ingestion job ing-123 throughout the guide. The document is the stored input. The job is our responsibility to process that input. The request is only the HTTP interaction used to submit it. Those three lifetimes need not end together: the connection may close while the document and unfinished job remain.

We choose to track the ingestion until it reaches READY or FAILED. We have not promised that every PDF will succeed: a corrupt document may be impossible to process. We have promised that acknowledging responsibility will not silently discard its work. That is the shared correctness requirement, including in this first explanation.

02

Start without a queue

The baseline keeps the upload request open through every required processing stage.

The simplest path validates and stores the PDF, performs OCR, runs required checks, creates embeddings, updates the search index, and then responds. It can be a sensible first implementation when volume is low and those steps reliably fit the user’s wait. Avoid treating the absence of a broker as an automatic design flaw.

Hand-drawn Excalidraw diagram showing the synchronous request path from the client and API gateway through the ingestion service, OCR, compliance, embeddings, and search index before returning 200 OKHand-drawn Excalidraw diagram showing the synchronous request path from the client and API gateway through the ingestion service, OCR, compliance, embeddings, and search index before returning 200 OK
Naive synchronous path. Original video diagram. Open the image at full size; scroll horizontally on smaller screens.

The response returns only after OCR, compliance, embeddings, and search indexing have finished.

Hand-drawn Excalidraw diagram showing several clients converging on the API gateway and ingestion service while requests remain open through slow OCR, compliance, throttled embeddings, and search indexingHand-drawn Excalidraw diagram showing several clients converging on the API gateway and ingestion service while requests remain open through slow OCR, compliance, throttled embeddings, and search indexing
What breaks under load. Original video diagram. Open the image at full size; scroll horizontally on smaller screens.

Concurrent clients keep requests open while slow OCR and throttled embeddings constrain the processing path.

Synchronous does not mean that the server handles only one request at a time. It may process many uploads concurrently. Each request still waits for its own slowest stage. If the embedding dependency becomes slow, those requests retain connections and other resources while waiting. New requests can arrive faster than this path finishes them.

A client timeout makes that coupling visible. The client no longer knows whether the upload failed, is still processing, or completed after the connection closed. A retry can add another attempt. Before changing the architecture, explain the actual problem: the request’s lifetime is tied to processing that the product already permits to happen later.

03

Separate acceptance from processing

Return acceptance only after the input and its pending work have a recoverable home.

Move OCR, checks, embeddings and indexing after the response. Keep request validation, durable input storage and the recoverable ingestion record before it. Otherwise, the API could say “accepted,” crash, and leave no process able to discover the work it owes the user.

Hand-drawn Excalidraw diagram separating the client, API gateway, ingestion service, validation, and 202 Accepted response from later OCR, compliance, embeddings, and search indexingHand-drawn Excalidraw diagram separating the client, API gateway, ingestion service, validation, and 202 Accepted response from later OCR, compliance, embeddings, and search indexing
The asynchronous boundary. Original video diagram. Open the image at full size; scroll horizontally on smaller screens.

Validation and the ingestion record belong before the response. OCR, compliance, embeddings, and indexing can finish later.

202 response
{
  "ingestion_id": "ing-123",
  "status": "ACCEPTED",
  "status_url": "/ingestions/ing-123"
}

One concrete implementation stores doc-456 in object storage, then commits ing-123 and a pending publication record together in a database transaction. A dispatcher reads committed pending records and publishes messages. If the accepting API crashes, another dispatcher can still find the pending record. The dispatcher may publish twice if it loses an acknowledgment, so consumers must tolerate repetition.

The response can be 202 Accepted with ing-123 and a status URL. HTTP 202 says processing is not complete; it does not guarantee eventual success. Our application adds the stronger promise that accepted input and recoverable work are durable. The status URL lets the client distinguish ACCEPTED, QUEUED, PROCESSING, READY and FAILED without keeping the original connection open.

04

Follow the queue-backed path

The queue distributes job messages; workers perform document processing.

The dispatcher publishes a small message containing ing-123, doc-456, the tenant identity and an object-storage reference. It does not put the PDF bytes in the message. Object storage owns the input, the job record owns status, and the queue tells a worker which job is available. These roles let a replacement worker find the same work without depending on the API process’s memory.

Hand-drawn Excalidraw diagram showing a client, API gateway, ingestion service, object storage, status store, job queue, worker pool, and the processing path to the search indexHand-drawn Excalidraw diagram showing a client, API gateway, ingestion service, object storage, status store, job queue, worker pool, and the processing path to the search index
Queue-backed ingestion. Original video diagram. Open the image at full size; scroll horizontally on smaller screens.

The API returns 202 Accepted after durable acceptance, then the queue holds small pending jobs until workers can run OCR, checks, embeddings, and indexing.

Representative queue message
{
  "ingestion_id": "ing-123",
  "document_id": "doc-456",
  "tenant_id": "tenant-7",
  "object_key": "uploads/doc-456.pdf"
}
Hand-drawn Excalidraw diagram showing a job queue delivering work to a worker, the worker fetching from object storage, running OCR compliance and embeddings, writing the search index and status store, then acknowledging after successHand-drawn Excalidraw diagram showing a job queue delivering work to a worker, the worker fetching from object storage, running OCR compliance and embeddings, writing the search index and status store, then acknowledging after success
Worker processing and job status. Original video diagram. Open the image at full size; scroll horizontally on smaller screens.

A worker fetches the PDF, marks the job as processing, performs the pipeline, marks it ready, and acknowledges the queue only after success. At-least-once delivery makes idempotent processing necessary.

TermMeaning in this design
RequestThe client's HTTP interaction with the ingestion service
JobThe durable unit of document-ingestion work
MessageThe queue entry announcing that a job is ready
DispatcherThe process that publishes recoverable pending jobs to the queue
QueueThe component that stores and distributes pending messages
WorkerThe process that executes or coordinates the ingestion job

The worker fetches the durable input, records that processing has started, and performs the pipeline. It records the successful result durably before acknowledging its queue delivery. READY must mean the required result is actually available to the product; merely starting indexing is insufficient if search still cannot use the document.

Suppose completion succeeds but the worker crashes before acknowledging. The queue can deliver the job again. The replacement must recognize completed work or repeat safe operations, rather than create duplicate index entries. The queue has preserved delivery, but the application still owns the effect of processing twice. The separate idempotency guide develops that mechanism.

05

Reason about waiting and capacity

A queue makes a finite backlog durable; it cannot supply missing processing throughput.

Suppose, only for illustration, ten comparable PDFs arrive each second while workers finish six each second for twenty seconds. Forty more arrive than finish in each ten-second interval, so the backlog grows by eighty PDFs over the burst. The numbers describe a simplified workload with constant rates and no retries, rather than measured performance.

Temporary burst: the backlog drains

Arrival falls below processing

Uploads briefly arrive faster than workers can process them. Once the spike ends, the workers keep consuming jobs until the queue is empty.

1. many PDFs arrive2. input rate falls3. backlog drainsPDF uploadstemporary spike8 PDFs injob queue5 jobs outworkersconsume jobsqueue depth returns to zerothe spike ends, so processing eventually catches up

Temporary burst

Sustained overload: the backlog keeps growing

Arrival stays above processing

Workers remain busy, but new jobs arrive faster than they finish. The queue eventually reaches a limit, so the system must scale, throttle, or reject work.

1. PDFs keep arriving2. input stays > output3. limit is reachedPDF uploadssustained rate14 PDFs injob queue4 jobs outworkersstill processingqueue limitscale, throttle, or reject new workthe workers stay busy, but the system never catches up

Sustained overload

After the burst, arrivals fall to two per second while completions remain six. The net drain rate is four per second, so those eighty waiting PDFs need twenty seconds to clear. Dividing by six would ignore the continuing arrivals. If ten keep arriving indefinitely, the backlog grows indefinitely until some storage, waiting or admission limit is reached.

Adding workers can increase useful throughput only if the shared dependencies can sustain their calls. Twenty workers do not help if a single embedding-service limit still allows six documents per second. The queue changes where work waits and allows controlled consumption. Capacity, reduced work per document or reduced accepted demand must resolve sustained overload.

06

Choose from required behavior

Independent ingestion jobs make a work queue a reasonable starting abstraction.

For this example, jobs are independent, one logical worker pool performs them, global ordering is unnecessary, and the product does not need retained upload-event history for replay. Amazon SQS Standard is a reasonable AWS implementation of that work-queue need. This is a choice from the stated behavior, not a claim that every background task needs SQS.

Hand-drawn Excalidraw decision diagram comparing SQS Standard for independent jobs with Kafka for retained history, replay, partition ordering, and multiple consumer groupsHand-drawn Excalidraw decision diagram comparing SQS Standard for independent jobs with Kafka for retained history, replay, partition ordering, and multiple consumer groups
Choose SQS or Kafka from the required behavior. Original video diagram. Open the image at full size; scroll horizontally on smaller screens.

Independent jobs with no replay or global-ordering requirement point toward SQS Standard. Retained history, replay, partition ordering, or multiple consumer groups make Kafka more compelling.

RequirementStarting abstraction
Independent background jobs claimed by a worker poolWork queue such as SQS
Retained event history and replayEvent log such as Kafka
Several independent consumer groups need the same eventEvent log such as Kafka
Ordering within a defined stream is centralA system with explicit ordering semantics

SQS Standard can deliver a message more than once and uses best-effort ordering. A worker therefore must tolerate repeats and must not rely on uploads being processed in a global sequence. A broker’s delivery behavior is part of the design contract; adding its name to a diagram does not remove application recovery work.

If several independent systems need to read the same retained stream, or rebuilding a projection by replay becomes central, Kafka becomes more compelling. Kafka retains events according to topic configuration and supports independent consumer groups. Ordering is within a partition. Those changed requirements justify evaluating another abstraction; they do not imply Kafka is automatically required for high traffic.

07

Make the decision explainable

Introduce a queue only after connecting its benefits and limits to the product.

Start with request lifecycle: can the user receive acceptance before completion? Then ask whether the work should survive a process failure, whether finite bursts exceed processing rate, and whether consumers need bounded concurrency to protect a dependency. These questions explain the asynchronous boundary, recoverable waiting and controlled consumption we actually used.

Next identify the cost. We added a dispatcher, messaging infrastructure, worker recovery and a status path. The user now waits for a later outcome. We must observe pending handoff age and unfinished work as well as API latency. A short HTTP response is not proof that documents become searchable within an acceptable time.

The decision can reverse. A tiny internal tool with short predictable processing may keep the synchronous path. A product that requires immediate search results may need a smaller synchronous operation or a changed user experience. Our final explanation is specific: this PDF product permits later searchability, so durable jobs and controlled workers solve a real lifecycle and burst problem, while processing capacity remains a separate obligation.

Comments

Drag IL Pet to any screen edge. Use Shift with an arrow key to change edges, arrow keys to move along an edge, and Home to reset.