Skip to content

RocketMQ: Requeue does nothing, so the pump's delivery budget can never be exhausted #4353

Description

@iancooper

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions