Skip to content

fix(rust): Do not drop messages in RunTaskInThreads under backpressure - #584

Merged
untitaker merged 1 commit into
mainfrom
fix/run-task-in-threads-carried-over-loss
Oct 22, 2026
Merged

untitaker merged 1 commit into
mainfrom
fix/run-task-in-threads-carried-over-loss

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Fixes #583.

RunTaskInThreads::poll could silently drop messages when the next step applied backpressure. There were two ways this happened:

  1. Several finished tasks rejected in one poll(). The loop over finished handles kept going after a MessageRejected, so each rejection overwrote message_carried_over. This has been there since the strategy was first added (feat(rust-consumer): Add RunTaskInThreads strategy snuba#4537), and it's the case the Rust RunTaskInThreads loses pending messages when downstream applies backpressure #583 repro hits.
  2. Retry of the carried-over message fails, then the loop forwards another task. The original code returned early after a failed retry. That early return was dropped when the if let became a match in feat(rust): Strategy can return InvalidMessage error in poll, join, submit snuba#4929, so since then the loop runs anyway and can overwrite the pending message.

The fix stops forwarding finished tasks while message_carried_over is set. Later results stay in order in the handles queue until the carried-over message is accepted, and submit() already applies backpressure upstream while a message is carried over.

Tests

I added test_backpressure_with_multiple_finished_tasks_does_not_drop_messages. It submits 3 messages, waits for all tasks to finish, polls 3 times while the next step rejects everything, then unblocks and joins. It asserts that [1, 2, 3] are delivered in order.

To check that the test covers both paths, I ran it against three broken variants. Each one failed, delivering [3]:

  • no fix
  • only path 1 fixed (break after an in-loop rejection)
  • only path 2 fixed (the original early return after a failed retry)

Checks run locally:

Not in scope

submit() checks self.handles.len() > self.concurrency, which lets one more task run than the configured concurrency allows. It predates both bugs. Changing it affects throughput, so I'd rather handle it separately.

via markus.

--

View Junior Session [Sentry]

RunTaskInThreads::poll kept forwarding completed tasks after the next
step rejected a message, so a second rejection overwrote
message_carried_over and silently dropped the earlier message. The same
overwrite happened when retrying an existing carried-over message failed
and the loop then forwarded another completed task.

Stop forwarding completed tasks while a message is carried over. Later
results stay in order in the handles queue until the carried-over
message is accepted.

Fixes #583

Co-Authored-By: Markus Unterwaditzer <markus@sentry.io>
@sentry-junior
sentry-junior Bot requested review from a team as code owners October 20, 2026 15:43
@untitaker
untitaker merged commit 9aa2f28 into main Oct 22, 2026
22 checks passed
@untitaker
untitaker deleted the fix/run-task-in-threads-carried-over-loss branch October 22, 2026 11:25
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.

Rust RunTaskInThreads loses pending messages when downstream applies backpressure

1 participant