Skip to content

QoS1 backlog: clean-start reconnect racing an in-flight publish can deliver one stray message #150

Description

@fabracht

Found in the QoS1 backlog/takeover rework (branch worktree-qos1-backlog, commit 6024b21 + round-3 follow-ups), round-3 review finding #5. Not a regression from main; a residual edge case in the new takeover path.

Severity: major (spec violation), but a narrow timing race. No message loss, duplication, crash, or unbounded memory.

Scenario: Client c (handler A1) is subscribed to t and is behind / has a full QoS1 lane. A publish to t snapshots a delivery plan for A1 under the router read locks, then drops the locks. In the small window before that plan executes, c reconnects with clean_start=true (handler A2): register_session strips A1's subscriptions and A2 binds (clear(None), fresh session). The stale plan then executes, finds A1's lane closed, and falls through to queue_behind, pushing the message onto c's queue with a fresh sequence number. A2 drains it and delivers it — a message routed under the prior session's subscription reaches a clean session that never subscribed to t.

Why it isn't already fixed: A sequence cutoff can't help — the stale push gets a fresh seq after the clean bind. The correct fix needs the delivery plan to carry the generation it was built under and to be dropped on mismatch only for clean-start takeovers (dropping on a resumed takeover would lose a message the resumed session legitimately should get, since the publisher's PUBACK already committed).

Proposed fix: Tag DeliveryPlan with the source generation; in execute_plan, before queue_behind, drop the message if the client's current generation differs and that generation belongs to a clean-start session. Location: crates/mqtt5/src/broker/router.rs (plan_delivery / execute_plan / queue_behind).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions