Skip to content

[fix][broker] Clear delayed delivery state before resetting the cursor - #26420

Open
nodece wants to merge 1 commit into
apache:masterfrom
nodece:fix-reset-clear-delayed-messages
Open

[fix][broker] Clear delayed delivery state before resetting the cursor#26420
nodece wants to merge 1 commit into
apache:masterfrom
nodece:fix-reset-clear-delayed-messages

Conversation

@nodece

@nodece nodece commented Aug 25, 2026

Copy link
Copy Markdown
Member

Motivation

PersistentSubscription#resetCursorInternal reset the cursor without touching the delayed delivery tracker. The tracker state is a derived cache of the backlog (the source of truth is deliverAtTime in the message metadata), and a reset moves the consumption baseline, so the whole derivation is invalidated:

  • bucket snapshots, index bits and queued entries from before the reset survived into the replay, and re-added messages deduped against the stale index instead of being re-tracked cleanly;
  • in-flight trims/loads/deletes from before the reset could race the replayed state;
  • the bucket cursor properties stayed on the cursor, so a later tracker recovery would load pre-reset buckets.

clearBacklog and unsubscribe already clear the delayed state for the same reason; the reset path was the odd one out.

Modifications

  • resetCursorInternal now clears the delayed messages and waits for the clear to settle after disconnecting consumers and before asyncResetCursor; a clear failure unfences the subscription and fails the reset.
  • Without a dispatcher there is no in-flight work and the recovered bucket snapshots stay valid for the replay (re-added messages dedup through the index bitmap), so there is nothing to clear.

Verifying this change

  • Make sure that the change passes the CI checks.

This change added tests and can be verified as follows:

  • BucketDelayedDeliveryTest#testResetCursorClearsDelayedMessages: tracks 100 delayed messages, resets the cursor to the earliest position and verifies the tracker count is 0 and the bucket cursor properties are removed. Without the fix the assertion fails: the tracker still reports 100 delayed messages and the bucket cursor property survives the reset.

Does this pull request potentially affect one of the following parts:

Check the boxes that apply

  • Dependencies (add or upgrade a dependency)
  • The public API
  • The schema
  • The default values of configurations
  • The threading model
  • The binary protocol
  • The REST endpoints
  • The admin CLI options
  • The metrics
  • Anything that affects deployment

@nodece
nodece force-pushed the fix-reset-clear-delayed-messages branch from f7e0723 to 0b30bc7 Compare August 25, 2026 10:01
resetCursorInternal() reset the cursor without touching the delayed
delivery tracker, although a reset moves the consumption baseline the
tracker state was derived from: bucket snapshots, index bits and queued
entries from before the reset survived into the replay, and in-flight
trims/loads/deletes could race the replayed state. Clear the delayed
messages and wait for the clear to settle after disconnecting consumers
and before asyncResetCursor, with the whole reset flow chained into a
single error path. Without a dispatcher there is no in-flight work and
the recovered bucket snapshots stay valid for the replay, so there is
nothing to clear.
@nodece
nodece force-pushed the fix-reset-clear-delayed-messages branch from 0b30bc7 to 15f944a Compare August 25, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant