BrighterCommand / BrighterCommand/Brighter
RocketMQ: Requeue does nothing, so the pump's delivery budget can never be exhausted
- Dominant language
- C#
- Stars
- 2.5k
- Forks
- 296
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 21
Description
## 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:
```csharp
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.
Contributor guide
Assessment
This issue has not been assessed yet.