Class OrderedQueue<T>
- Type Parameters:
T- payload type
- All Implemented Interfaces:
AutoCloseable
try (OrderedQueue<Order> orders = mq.ordered("orders", Order.class)
.partitions(8)
.keyedBy(Order::customerId)
.declare()) {
orders.send(order);
orders.consume(message -> ledger.post(message.payload()));
}
What this actually costs
Ordering and parallelism are opposed, and no amount of API hides that. What this does is make the standard trade cheap: the key decides a partition, each partition is a queue, and each queue has exactly one consumer. Eight partitions gives eight-way parallelism and ordering within any single key — which is almost always the ordering anyone actually wanted. Two orders for one customer are sequenced; two orders for different customers are not, and never needed to be.
Throughput is therefore bounded by the busiest key, not by the number of partitions. One very hot customer is one queue, one consumer, and no way to go faster without giving up the guarantee.
Retries reorder, so the retry ladder is not available here
This is the part usually left unsaid. If message five fails and is republished to come back
in thirty seconds, message six is handled first and the sequence is broken. Every ordered
consumer has to choose between blocking the partition and breaking the order, so
OrderedQueue.OnFailure makes the choice explicit and offers nothing that quietly does
neither.
RETRY_IN_PLACE sleeps in the handler, which is a bug in an unordered consumer and
is the right answer here: the partition is meant to stall, because the alternative is
delivering message six before message five.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic enumWhat happens when a handler fails. -
Method Summary
-
Method Details
-
queueFor
- Returns:
- the queue backing a partition
-
name
- Returns:
- the logical name, which is also the exchange
-
partitions
public int partitions()- Returns:
- how many partitions there are
-
send
- Parameters:
payload- the message- Returns:
- which partition it went to, which is worth having in a log when a sequence looks wrong
-
send
- Parameters:
payload- the messageenvelope- metadata to publish with, ornull- Returns:
- the partition it went to
-
consume
Starts one consumer per partition.One, deliberately. A second consumer on a partition would take the next message while the first was still working, and the ordering would be gone.
- Parameters:
handler- called for each message, in order within any one key- Returns:
- this queue
-
haltedPartitions
- Returns:
- partitions stopped by a handler failure
-
handled
public long handled()- Returns:
- how many messages have been handled successfully
-
failed
public long failed()- Returns:
- how many handler attempts have failed
-
skipped
public long skipped()- Returns:
- how many messages were given up on; only ever non-zero with
OrderedQueue.OnFailure.SKIP
-
queues
- Returns:
- the queues behind the partitions, in order
-
close
public void close()- Specified by:
closein interfaceAutoCloseable
-
toString
-