Summary
RocketMessageConsumer.Requeue is a no-op. A message whose handler keeps deferring is therefore
redelivered indefinitely and is never dead-lettered — the pump's delivery budget (RequeueCount)
cannot run down, and no Reject is ever issued.
Found by the FR-23 conformance behaviour (requeue budget exhausted to DLQ) in #4240 / #4297, which
drives a real Brighter pump. The cell stays Deferred on this.
The code
src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageConsumer.cs:179 — the method resolves
the MessageView from the bag and returns true. That is all of it; the one call that would act on
the broker is commented out:
public bool Requeue(Message message, TimeSpan? delay = null)
{
if (!message.Header.Bag.TryGetValue("ReceiptHandle", out var handler) || handler is not MessageView view)
{
return false;
}
// Waiting for next RocketMQ C# version, due an issue on ChangeInvisibleDuration
// consumer.ChangeInvisibleDuration(view, TimeSpan.Zero);
return true;
}
So the message simply stays invisible until its lease lapses and the broker re-serves the stored
copy. HandledCount is read from the message's published properties (ReadHandledCount, :422) and
written only on send (RocketMqMessageProducer.cs:157), so every redelivery arrives reading the value
that was originally published. The pump bumps it to 1, HandledCountReached(RequeueCount) is never
true, and nothing reaches the DLQ.
Measured, and it is not a timing problem
RocketMQ redelivers only when its 10 s invisibility lease lapses, so a budget of 3 could plausibly
outrun the conformance suite's 30 s ceiling. It does not:
| ceiling |
result |
| 30 s (both variants) |
MT_NONE — nothing on the DLQ |
| 150 s (Reactor, ~15 redeliveries against a budget of 3) |
still MT_NONE |
The budget does not run down slowly. It does not run down at all.
Why this is the most complete instance of a family
This is the third transport where FR-23 has found the same root cause — a requeue that does not
persist the delivery count cannot exhaust a Brighter-side budget:
| transport |
requeue mechanism |
consequence |
| AWS SQS (#4341) |
ChangeMessageVisibility |
budget inert, but maxReceiveCount redrive dead-letters the message anyway |
| GCP Pub/Sub |
ModifyAckDeadline(…, 0) |
same, with MaxDeliveryAttempts as the redrive |
| RocketMQ |
nothing at all |
no broker-side redrive is wired either, so the message is never dead-lettered by anyone |
The transports whose FR-23 cells pass (Redis, Kafka ×3, MSSQL, Postgres, RMQ ×3) all republish on
requeue, carrying the updated header — which is exactly why their budgets run down.
Suggested fix
Either restore ChangeInvisibleDuration once the RocketMQ C# client issue the comment refers to is
resolved and make the redelivered message carry an incremented delivery count, or map RocketMQ's own
delivery-attempt property into HandledCount on receive so the budget runs off the broker's counter.
The second needs no client fix.
Impact
Any RocketMQ consumer whose handler defers loops for ever. Blocks 1 FR-23 conformance cell.
Summary
RocketMessageConsumer.Requeueis a no-op. A message whose handler keeps deferring is thereforeredelivered indefinitely and is never dead-lettered — the pump's delivery budget (
RequeueCount)cannot run down, and no
Rejectis ever issued.Found by the FR-23 conformance behaviour (
requeue budget exhausted to DLQ) in #4240 / #4297, whichdrives a real Brighter pump. The cell stays
Deferredon this.The code
src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageConsumer.cs:179— the method resolvesthe
MessageViewfrom the bag and returnstrue. That is all of it; the one call that would act onthe broker is commented out:
So the message simply stays invisible until its lease lapses and the broker re-serves the stored
copy.
HandledCountis read from the message's published properties (ReadHandledCount,:422) andwritten only on send (
RocketMqMessageProducer.cs:157), so every redelivery arrives reading the valuethat was originally published. The pump bumps it to 1,
HandledCountReached(RequeueCount)is nevertrue, and nothing reaches the DLQ.
Measured, and it is not a timing problem
RocketMQ redelivers only when its 10 s invisibility lease lapses, so a budget of 3 could plausibly
outrun the conformance suite's 30 s ceiling. It does not:
MT_NONE— nothing on the DLQMT_NONEThe budget does not run down slowly. It does not run down at all.
Why this is the most complete instance of a family
This is the third transport where FR-23 has found the same root cause — a requeue that does not
persist the delivery count cannot exhaust a Brighter-side budget:
ChangeMessageVisibilitymaxReceiveCountredrive dead-letters the message anywayModifyAckDeadline(…, 0)MaxDeliveryAttemptsas the redriveThe transports whose FR-23 cells pass (Redis, Kafka ×3, MSSQL, Postgres, RMQ ×3) all republish on
requeue, carrying the updated header — which is exactly why their budgets run down.
Suggested fix
Either restore
ChangeInvisibleDurationonce the RocketMQ C# client issue the comment refers to isresolved and make the redelivered message carry an incremented delivery count, or map RocketMQ's own
delivery-attempt property into
HandledCounton receive so the budget runs off the broker's counter.The second needs no client fix.
Impact
Any RocketMQ consumer whose handler defers loops for ever. Blocks 1 FR-23 conformance cell.