SQS FIFO queues don’t actually guarantee order; they guarantee deduplication and ordered processing within a message group.

Let’s see it in action. Imagine we’re processing incoming orders for an e-commerce site. We want to ensure that if two orders come in for the same customer, they are processed in the exact sequence they were received.

First, we need to create a FIFO queue.

aws sqs create-queue --queue-name my-order-processing-fifo.fifo --attributes FifoQueue=true,ContentBasedDeduplication=true

Notice FifoQueue=true and ContentBasedDeduplication=true. The .fifo suffix is mandatory for FIFO queues. ContentBasedDeduplication is a convenience; if false, you’d need to provide MessageDeduplicationId manually.

Now, let’s send some messages. We’ll simulate two orders for customer cust-123 and one for cust-456.

aws sqs send-message --queue-url <YOUR_QUEUE_URL> --message-body '{"orderId": "ORD1001", "customerId": "cust-123"}' --message-group-id cust-123
aws sqs send-message --queue-url <YOUR_QUEUE_URL> --message-body '{"orderId": "ORD1002", "customerId": "cust-123"}' --message-group-id cust-123
aws sqs send-message --queue-url <YOUR_QUEUE_URL> --message-body '{"orderId": "ORD2001", "customerId": "cust-456"}' --message-group-id cust-456

The critical parameter here is message-group-id. All messages sent with the same message-group-id will be processed in the order they were sent to that specific group. Messages from different groups can be processed in any order relative to each other.

When we poll for messages:

aws sqs receive-message --queue-url <YOUR_QUEUE_URL> --max-number-of-messages 10 --wait-time-seconds 20

We will always receive ORD1001 before ORD1002 for cust-123. ORD2001 for cust-456 might appear before, between, or after the messages for cust-123, depending on SQS’s internal scheduling.

The problem this solves is avoiding race conditions in downstream processing. If ORD1002 for cust-123 arrived at our processing service before ORD1001, we might try to ship an item that was canceled in ORD1001. By ensuring ordered processing per customer, we maintain data integrity.

Internally, SQS uses a distributed locking mechanism. When you send a message with a message-group-id, SQS effectively acquires a lock for that group. Until that lock is released (by deleting the message after processing), no other message for that same message-group-id can be delivered. This ensures that only one consumer is processing messages from a given group at any time, and SQS guarantees delivery order to that consumer.

The MessageDeduplicationId is what prevents duplicate messages. If ContentBasedDeduplication is true, SQS generates a hash of the message body. If you provide MessageDeduplicationId manually, you have full control. Crucially, this deduplication ID is unique per queue, not per message group. So, if you send two identical messages with the same MessageDeduplicationId to different message groups, only the first one will be processed.

The most surprising thing about FIFO queues is how they handle throughput. While standard queues offer a theoretical 300 messages per second, FIFO queues have a lower baseline of 3,000 messages per second per API action (SendMessage, ReceiveMessage, DeleteMessage) and per message group. This means a single FIFO queue can handle up to 300 messages per second (3,000 messages/sec / 10 message groups, assuming 10 groups are active). If you have many message groups, the aggregate throughput can be much higher, but you’re still limited by the 3,000/sec per group. This is achieved by SQS managing the internal locks more efficiently for a higher number of concurrent groups.

The next concept you’ll encounter is managing message visibility timeouts effectively to avoid message redelivery and processing conflicts, especially when dealing with high-throughput message groups.

Want structured learning?

Take the full Sqs course →