Ordering and concurrency
Edit this pageConsumer · Related pages
Consumer instances can process several messages at once. This lets unrelated work move ahead, but messages can finish in a different order from their message ids.
SQLStreams uses message keys to identify related work and concurrency policies to control whether that work can run together or must wait. These rules apply within a consumer group, across its running instances.
Message keys identify related work
Suppose a producer has committed three messages to the orders.status
stream. Each message updates an order’s status. To identify updates for
the same order, the producer assigns the order id as each message’s
MessageKey.
| Message id | Message key | Requested update |
|---|---|---|
| 1 | order-101 |
Set status to paid. |
| 2 | order-101 |
Set status to shipped. |
| 3 | order-102 |
Set status to paid. |
Messages 1 and 2 affect the same order. If message 2 finishes first and
message 1 finishes afterward, the order ends up marked paid even though
it has shipped. Message 3 updates a different order and can run independently.
A key alone does not impose any ordering. The concurrency policy determines what SQLStreams does with messages sharing the same key.
Three policies control when a message can start
For this example, assume the same policy applies to all three messages.
| Policy | While message 1 is running | If message 1 fails and waits to retry |
|---|---|---|
parallel |
Message 2 can run at the same time. | Message 2 can run before the retry. |
exclusive |
Message 2 waits. | Message 2 can run before the retry. |
ordered |
Message 2 waits. | Message 2 keeps waiting for message 1 to succeed or be marked dead. |
Parallel is the default. It permits messages with the same key to overlap. Exclusive prevents that overlap while a consumer handler holds the key’s lease, but it does not preserve message-id order.
Ordered processes messages sharing a key in message-id order, including through retries. For the order-status updates above, this keeps message 1’s retry from overwriting the status applied by message 2.
Message 3 has a different key, so none of these policies requires it to wait for messages 1 or 2. It can run when consumer capacity is available.
Ordered messages wait through a failure
With the ordered policy, suppose the consumer handler for message 1 cannot
reach the application’s database:
- Message 1 fails. Its consumer handler returns an error, and SQLStreams records the failure for a later retry.
- Message 2 waits. Its consumer handler cannot start while message 1
still needs a retry. Message 3 can proceed because it updates
order-102. - Message 1 succeeds on retry. Once SQLStreams records its success,
message 2 becomes eligible to run and set
order-101toshipped.
If or when message 1 is marked dead, message 2 becomes eligible. Ordered processing does not require every earlier message to succeed before later work can proceed.
The cost is that one slow or repeatedly failing message holds up later work with the same key. Adding more consumer instances helps other keys, but it cannot make that ordered key run concurrently.
Related topics
- Concurrency settings — Choose a group default or override the policy supplied by producers.
- Produce options — Assign a message key and per-message policy.
- Consume options — Set an instance’s
MessageConcurrency. This limits fresh-message processing capacity and does not establish ordering across the group. - Retries and failed messages — How a failed message retries and when it becomes dead.