Things Engineers Commonly Get Wrong About SQS
The worked examples are added in build phase 8.
Every entry follows the same shape: the wrong idea → the correct idea → why → an example.
Most of these are not beginner mistakes. Several appear in production systems built by experienced engineers, and at least four appear in widely-cited tutorials that are simply out of date.
1. "SQS pushes messages to consumers"
Correct idea: SQS is strictly pull. It never initiates a connection to your code.
Why: Push would require SQS to know your consumers' addresses, health and capacity, and would let a fast producer overwhelm a slow consumer. Pull lets the consumer set its own rate — which is what makes backpressure possible at all.
The confusion comes from Lambda "triggers". The Lambda service runs pollers that call
ReceiveMessage; SQS still pushes nothing. See Module 19.
2. "One consumer receives one message at a time"
Correct idea: ReceiveMessage returns up to MaxNumberOfMessages (1–10) messages per call, and
a consumer can run many threads processing many messages concurrently.
Why: The confusion conflates the API call with the unit of work. One call can carry ten messages; one consumer can process hundreds concurrently. See Module 13.
3. "Batch means SQS groups messages together permanently"
Correct idea: A batch is a transport optimisation on one request. The messages have no relationship to each other before or after the call. Each has its own receipt handle, its own visibility timer, its own retry count and its own fate.
Why: Batching amortises network and billing cost per API call. It is not a grouping construct and it is not transactional — there is no "batch succeeded" or "batch failed" at the queue level.
4. "ReceiveMessage returns 10 messages by default"
Correct idea: The default is 1.
Why: It is a silent 10× cost and throughput bug. The parameter must be set explicitly.
See aws-facts.md §1.
5. "Deleting happens automatically after processing"
Correct idea: DeleteMessage is an explicit call. Nothing deletes a message on your behalf
except retention expiry — and that is data loss, not success.
Why: SQS cannot know whether your processing succeeded. Explicit deletion is the acknowledgement, and it is what makes at-least-once delivery safe across a consumer crash.
(Lambda and Spring Cloud AWS delete for you — but they are doing it, not SQS.)
6. "Visibility timeout means the message is deleted"
Correct idea: The message stays in the queue. It is temporarily invisible to other consumers. If you do not delete it, it becomes visible again.
Why: Invisibility is a lease, not a removal. The lease exists so that a crashed consumer's work is not lost. See Module 08.
7. "A message disappearing means it was processed"
Correct idea: A message can leave your view for four different reasons: it was deleted, it is in flight with another consumer, it moved to the DLQ, or its retention period expired.
Why: Retention expiry is silent — no event, no metric spike, no DLQ entry. It is the only way
SQS loses a message, and it is invisible unless you alarm on ApproximateAgeOfOldestMessage.
See Module 05.
8. "Standard SQS guarantees ordering"
Correct idea: Standard queues make a best-effort attempt at ordering and guarantee nothing.
Why: Messages are stored across many servers with no global sequencer. There is also an explicit reordering behaviour: on a standard queue, a message received 3 or more times without deletion may be moved to the back of the queue. See Module 06.
9. "FIFO means exactly-once business processing"
Correct idea: AWS's "exactly-once processing" means SQS does not introduce duplicates into the
queue when you retry a SendMessage within the 5-minute deduplication window. Your consumer can
still process the same message twice — a crash before DeleteMessage guarantees it.
Why: Deduplication is a send-side property. Redelivery is a receive-side event. They are different halves of the system, and only one of them is covered. This is the single most consequential misreading of the SQS documentation. See Module 07 and Module 09.
10. "At-least-once delivery means duplicates are rare enough to ignore"
Correct idea: Duplicates are a normal operating condition, not an anomaly. Any consumer crash, any deploy without graceful shutdown, any processing overrun produces one.
Why: At-least-once is a guarantee about the minimum, with no bound on the maximum. Systems that "handle duplicates when they happen" handle them incorrectly, because they were never tested against one. See Module 09.
11. "Increasing batch size always increases throughput"
Correct idea: Throughput is batchSize ÷ cycleTime × consumers. A larger batch increases cycle
time; if it increases it proportionally, throughput does not move. Larger batches also increase the
work lost when a consumer dies mid-batch.
Why: Batch size reduces the number of API calls, not the time spent processing. See Module 15.
12. "More consumers always improve performance"
Correct idea: Adding consumers helps until you reach the next constraint — usually the downstream database, sometimes a quota, sometimes (on FIFO) the number of active message groups.
Why: SQS on standard queues is effectively unlimited, so the queue is almost never the bottleneck. Adding consumers to a downstream-bound system increases contention and reduces throughput. See Module 14.
13. "A DLQ fixes failed messages"
Correct idea: A DLQ is a quarantine. It stops a poison message from consuming capacity forever. Nothing is retried, repaired or replayed automatically.
Why: A DLQ with no alarm and no runbook is strictly worse than no DLQ — it converts a loud failure into a silent one. See Module 11.
14. "Long polling means messages are pushed"
Correct idea: Long polling means the consumer's request waits (up to 20 seconds) for a message to arrive, instead of returning empty immediately. The consumer is still asking.
Why: It is a held-open request, not a callback. It reduces empty responses and billed requests, and it lowers latency because the response is sent the instant a message becomes available. See Module 12.
15. "SQS and Kafka are interchangeable"
Correct idea: SQS consumption is destructive — read a message and it is gone. Kafka reads are non-destructive — the log persists and consumers track offsets, so the same data can be read again, by new consumers, from any point.
Why: Replay and independent consumer groups are structural properties of the storage model. No SQS configuration provides them. See Module 27.
16. "The maximum message size is 256 KB"
Correct idea: It is 1 MiB (1,048,576 bytes).
Why: This one is simply stale. It is repeated in a large number of tutorials and training
courses written before the limit was raised. Note separately that billing chunks payloads at
64 KB, so a 1 MiB message is billed as 16 requests.
See aws-facts.md §1 and §9.
17. "FIFO queues allow 20,000 in-flight messages"
Correct idea: 120,000, the same as standard queues.
Why: Also stale documentation, widely repeated. See aws-facts.md §3.
18. "FIFO throughput is 300 TPS, full stop"
Correct idea: 300 TPS per API action, per partition. With batching that is 3,000 messages/sec. With high-throughput mode enabled it reaches 70,000 TPS / 700,000 messages per second in the largest Regions — and 2,400 TPS in the smallest.
Why: The number is per-partition and region-dependent, so a single quoted figure is almost
always wrong. See aws-facts.md §4.
19. "If one message in a Lambda batch fails, only that message is retried"
Correct idea: By default the entire batch becomes visible again, including the messages that
succeeded. Enabling ReportBatchItemFailures and returning the failed message ids is what narrows
it to the actual failure.
Why: Lambda deletes the batch only on a clean return. A thrown exception means no deletes at all. With a non-idempotent consumer, one bad message in ten produces nine duplicate side effects. See Module 19.
20. "Billing is per message"
Correct idea: Billing is per request, and each 64 KB of payload counts as one request.
Why: It changes two things. Batching ten messages into one call costs roughly one tenth as much. And a 500 KB message costs 8× a small one for the same single send. See Module 18.
21. "@Retryable in Spring will eventually send the message to the DLQ"
Correct idea: Spring's in-process retry never returns the message to the queue.
ApproximateReceiveCount stays at 1, maxReceiveCount is never approached, and the DLQ never
receives it.
Why: SQS's retry mechanism is visibility expiry on an undeleted message. If Spring catches, retries and eventually acknowledges — or retries past the visibility timeout — SQS's own retry accounting is bypassed entirely. See Module 23.
22. "Raising the visibility timeout will stop the duplicates"
Correct idea: It reduces duplicates caused by processing overrun. It does nothing about duplicates caused by crashes, deploys, network partitions or replication — and it makes crash recovery slower by exactly the amount you raised it.
Why: Duplicate processing has several causes and the timeout addresses one. It is also the classic incident-under-pressure change that masks a real processing-time problem. See Module 08 and Module 26.
← Index: Course home