Repository navigation
GH-5120 A durable batch's scheduled retry is held in memory instead of lost - #5122
Merged
Merged
Conversation
…f lost A native batch on a durable queue is a carrier envelope with no inbox row; only its members are persisted. ScheduleRetry rescheduled it by UPDATE-ing the row for the carrier's id, which matched nothing, so the retry silently never ran and the members sat 'Incoming', owned by the live node. DurableReceiver now holds a batch carrier's retry in memory and re-enqueues the same carrier, so its Attempts keeps counting toward the policy's limit. The members' rows stay owned here while it is pending; a drain cancels pending batch retries before releasing those rows to another node. Also, the RDBMS and Oracle inboxes now warn when ScheduleExecutionAsync matches no row, rather than succeeding silently. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DCggXgxnG3EBXoR9rrq37U
This was referenced Oct 23, 2026
This was referenced Oct 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #5120.
Problem
A native batch (
BatchMessagesOf<T>().UseDurableInbox()) whose handler fails under aScheduleRetrypolicy was logged as rescheduled but never ran again. Its members' inbox rows satIncoming, attempts 0, noexecution_time, owned by the live node, which nothing reclaims until that node restarts.The batch is a carrier envelope with no inbox row of its own; only its members are persisted.
DurableReceiver.MoveToScheduledUntilAsyncrescheduled it throughScheduleExecutionAsync, anUPDATE ... where id = @idon the carrier's id. That matched nothing and succeeded silently.Fix
Batch retries are held in memory.
DurableReceivernow schedules a batch carrier's retry in memory, the same wayBufferedReceiverschedules everything, and re-enqueues the same carrier when it comes due.Attemptskeeps counting, so the policy runs out and dead letters the batch as a unit instead of retrying forever. Spreading the schedule across the member rows would have rebuilt a fresh carrier at attempt 0 every time.DrainAsynccancels pending batch retries before it releases the members' rows, so a late retry cannot run them alongside the node that picks them up.An update that matches nothing now warns. The RDBMS inbox (SQL Server, PostgreSQL, MySQL, SQLite) and Oracle now log a warning when
ScheduleExecutionAsyncupdates no row, instead of succeeding silently. RavenDB and Cosmos DB are not covered.Tests
SqlServerTestsBug_5120_scheduled_retry_of_a_durable_batch: the reporter's configuration.SqlServerTestsscheduling_an_envelope_with_no_inbox_row_5120: the warning is logged for a missing row and not for an existing one.CoreTestsdurable_batch_scheduled_retry_5120: the carrier is re-executed after its scheduled time; it is never rescheduled through the inbox; a pending retry does not fire after a drain. The first two are red without the fix.dotnet build wolverine.slnx -c Release -f net9.0: clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01DCggXgxnG3EBXoR9rrq37U