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,DeleteMessageBatchandChangeMessageVisibilityBatchcorrectly - 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 groupingSendMessageBatch— 10 entries max, each with its own entryId; the response hasSuccessfulandFailedlistsDeleteMessageBatch— 10 receipt handles; partial failure is normal and must be handledChangeMessageVisibilityBatch— 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
Failedwere 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
ReceiveMessagecall returning up to 10 messages (emphasising pull) - Diagram 31 —
SendMessageBatch/DeleteMessageBatchsequence 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
DeleteMessageBatchwith those handles - Consumer inspects the
Failedlist 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
ReceiveMessageresponses 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
MaxNumberOfMessagesas a ceiling on a request - Ignoring the
Failedlist in a batch response. Why → HTTP 200 with silent per-entry failures; messages you think you deleted come back. Instead → always inspect and retryFailed - 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
MaxNumberOfMessagesat 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
MaxNumberOfMessagesdo? - 🟢 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
Failedlist 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):
batchMaxNumberOfMessagesSendMessageBatchDeleteMessageBatchChangeMessageVisibilityBatchpartial batch failureworker
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:
SendMessageBatchwith per-entry ids and fullFailed-list handlingDeleteMessageBatchaccumulating 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 →