Learning/AWS SQS/13 — Batch Processing
Intermediate 40 min read outline
This chapter is an outline. The curriculum, learning objectives and structure are settled; the prose, diagrams and code are still being written. What is below is the plan for the chapter, not the chapter.

Batch Processing

The most-misunderstood topic in SQS, and the spec flags it as the one that must be exceptionally clear. Five words get conflated constantly — API call, message, batch, consumer, worker — and untangling them is the difference between a throughput calculation that works and one that is off by 10×.

1. What you will learn

  • Distinguish precisely between an API call, a message, a batch, a consumer and a worker
  • Explain why 'batch size 10' does not mean 'SQS pushes 10 messages to me'
  • Use SendMessageBatch, DeleteMessageBatch and ChangeMessageVisibilityBatch correctly
  • Handle partial batch failures without losing or duplicating work
  • Calculate the real cost and throughput effect of batching

2. Why this concept exists

  • Every SQS API call has network latency and a billed cost, regardless of whether it carries 1 message or 10
  • Batching amortises both — up to 10× fewer calls for the same work
  • But batching introduces partial failure, which is where the bugs live

3. Beginner explanation

  • Instead of asking for one message at a time, ask for ten
  • One question, ten answers — one tenth the network trips and one tenth the cost
  • SQS is not pushing you a group. You asked for up to ten, and you got however many were available

4. How it actually works

  • The five terms, defined:
  • API call — one HTTPS request to SQS. The billing unit and the latency unit
  • Message — one item in the queue. The unit of work and of retry
  • Batch — up to 10 messages carried by a single API call. A transport convenience, nothing more
  • Consumer — a process that polls. The unit of deployment
  • Worker/thread — a unit of concurrency inside a consumer. The unit of parallelism
  • ReceiveMessage(MaxNumberOfMessages = 10) — a request for up to 10. SQS returns what is available, which is often fewer. It is pull, not push, and the batch is not a persistent grouping
  • SendMessageBatch — 10 entries max, each with its own entry Id; the response has Successful and Failed lists
  • DeleteMessageBatch — 10 receipt handles; partial failure is normal and must be handled
  • ChangeMessageVisibilityBatch — same shape
  • The whole batch counts against the 1 MiB total message size for send batches
  • Partial failure: a batch API returns 200 OK with per-entry results. Entries in Failed were not processed. Ignoring that list is a top-three production bug
  • Batch != atomic. There is no batch transaction. Each message succeeds or fails independently

5. Diagram

  • Diagram 30 — one ReceiveMessage call returning up to 10 messages (emphasising pull)
  • Diagram 31 — SendMessageBatch / DeleteMessageBatch sequence with partial failure
  • Diagram 32 — the five terms on one picture: API call vs message vs batch vs consumer vs worker

6. Step-by-step flow

  • Consumer calls ReceiveMessage(MaxNumberOfMessages = 10, WaitTimeSeconds = 20)
  • SQS returns between 0 and 10 messages — each with its own receipt handle
  • Each message's visibility timer starts independently
  • Consumer processes them (serially, or in parallel across workers)
  • Consumer accumulates receipt handles for the ones that succeeded
  • Consumer calls DeleteMessageBatch with those handles
  • Consumer inspects the Failed list and retries those deletes
  • Messages that failed processing are simply not deleted — they return via visibility expiry

7. Configuration

  • MaxNumberOfMessages: 1–10, default 1
  • Batch entry limit: 10 for all batch APIs
  • Size limits apply to the batch as a whole → aws-facts.md §1
  • Lambda's batch size is a different setting on the event source mapping (Module 19)

8. Production considerations

  • Batching multiplies duplicate-processing blast radius: if your consumer dies mid-batch, all 10 messages come back, including the ones already done. Idempotency (Module 09) is mandatory
  • A batch shares a deadline: all 10 visibility timers started together, so slow message #1 eats the budget of message #10
  • Bigger batches are not always faster — they increase per-invocation latency and the amount of work lost on failure. The optimum is workload-specific
  • Batch deletes must be idempotent too: a deleted-then-retried handle is harmless, a missed delete is not
  • Empty ReceiveMessage responses are normal even with batching — never treat a short return as an error

9. Common mistakes

  • "SQS pushes batches to my consumer." Why → it is pull; you requested up to 10. Instead → understand MaxNumberOfMessages as a ceiling on a request
  • Ignoring the Failed list in a batch response. Why → HTTP 200 with silent per-entry failures; messages you think you deleted come back. Instead → always inspect and retry Failed
  • Treating a batch as a transaction. Why → there is no atomicity. Instead → per-message success tracking
  • Failing the whole batch when one message fails. Why → nine successful messages get reprocessed. Instead → delete the successes, leave the failure
  • Leaving MaxNumberOfMessages at 1 while proudly using long polling. Why → half the optimisation. Instead → both
  • Assuming a full batch of 10 always arrives. Why → SQS returns what is available. Instead → handle 0–10

10. Real-world example

  • A queue at 1,000 msg/s: unbatched = 2,000 API calls/s (receive + delete) ≈ $2,600/month. Batched at 10 = 200 calls/s ≈ $260/month. Same throughput, same latency, one order of magnitude cheaper

11. Interview questions

  • 🟢 What does MaxNumberOfMessages do?
  • 🟢 What is the maximum batch size in SQS?
  • 🟡 Does SQS push a batch of messages to a consumer? Explain what actually happens.
  • 🟡 What is the difference between an API call, a message and a batch?
  • 🔴 What happens if a consumer processes 7 of 10 messages and then crashes?
  • 🔴 How do you handle partial failure in DeleteMessageBatch?
  • 🔴 Why might increasing batch size reduce throughput?
  • ⚫ Design the batching strategy for a pipeline at 50,000 msg/s with a 2-second p99 latency budget.

12. Summary

  • A batch is a transport optimisation on a pull request, not a push and not a grouping
  • API call ≠ message ≠ batch ≠ consumer ≠ worker — the throughput math depends on keeping them straight
  • Batch APIs return per-entry results; the Failed list is not optional
  • Batching is ~10× cheaper and amplifies duplicate blast radius on crash
  • Combine with long polling; neither alone is enough

Authoring notes

Terms defined in this module (defined once here, linked from everywhere else):

  • batch
  • MaxNumberOfMessages
  • SendMessageBatch
  • DeleteMessageBatch
  • ChangeMessageVisibilityBatch
  • partial batch failure
  • worker

Diagrams to build:

  • Diagram 30 — batch receive
  • Diagram 31 — batch send/delete with partial failure
  • Diagram 32 — API call vs message vs batch vs consumer vs worker

Code samples:

  • SendMessageBatch with per-entry ids and full Failed-list handling
  • DeleteMessageBatch accumulating only successful receipt handles
  • A batch-aware consumer loop with per-message error isolation
  • A cost/throughput comparison table generated from real call counts

Mandatory "why?" answers:

  • Why does 'batch size 10' not mean SQS pushes 10 messages?
  • Why can increasing batch size reduce throughput?
  • Why does a batch API return 200 OK when some entries failed?

Facts to pull from _reference/aws-facts.md: batch max 10, MaxNumberOfMessages default 1, 1 MiB total, 64 KB billing chunk


Previous: 12 — Long Polling · Index: Course home · Next: 14 — Scaling Consumers