Fix DLQ replay loop for buffered local queues (GH-1942)#2538
Merged
jeremydmiller merged 3 commits intomainfrom Apr 20, 2026
Merged
Fix DLQ replay loop for buffered local queues (GH-1942)#2538jeremydmiller merged 3 commits intomainfrom
jeremydmiller merged 3 commits intomainfrom
Conversation
When a non-durable local queue persisted a failed message to the database-backed DLQ, marking the row replayable caused the durability agent to dispatch it back via BufferedLocalQueue.EnqueueDirectlyAsync. That path used the BufferedReceiver itself as the channel callback, whose CompleteAsync is a no-op, so the inbox row was never marked Handled and got re-recovered on every host restart. This fix mirrors the GH-1594 pattern used in ListeningAgent for transport-backed endpoints: wrap the recovered envelope in a LocalQueueRecoveryListener whose CompleteAsync marks the row as Handled, and route through IReceiver.ReceivedAsync so the existing _completeBlock invokes the wrapper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First pass routed the recovery path through IReceiver.ReceivedAsync, which fires _completeBlock eagerly at receipt time. That regressed SqlServerTests.*.should_reasign_incoming_envelope_to_owner_id because scheduled messages dispatched via runtime.EnqueueDirectlyAsync were being marked Handled before the test could observe them as Incoming (and before the handler had actually run). Instead: attach the LocalQueueRecoveryListener to envelope.Listener during EnqueueDirectlyAsync, and have BufferedReceiver.CompleteAsync (the pipeline's IChannelCallback) delegate to it. Now the inbox row is only marked Handled after the pipeline successfully completes, which matches DurableReceiver's semantics and preserves the durability guarantee for scheduled messages. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When built with the .NET 10 SDK (now required via global.json rollForward: latestMajor), WolverineWebApiFSharp.dll was ending up with a reference to FSharp.Core 10.0.0.0 even though Directory.Packages.props and the csproj pinned 9.0.303. The existing `<PackageReference Update="FSharp.Core" VersionOverride="9.0.303"/>` form was not overriding the SDK's implicit F# Core reference. At runtime, JasperFx's Roslyn-based codegen resolves FSharp.Core 9.0.0.0 (the package version), and the embedded 10.0.0.0 reference in the F# assembly then fails with CS1705 — manifesting as a 500 on end-to-end tests like `post_returning_fsharp_taskunit` and cascading into `verify_open_api_expectations` via the shared Alba fixture. Fix: set DisableImplicitFSharpCoreReference=true and use an explicit `<PackageReference Include="FSharp.Core"/>`, which picks up the centrally-managed 9.0.303. Verified locally: `WolverineWebApiFSharp.dll` now references FSharp.Core 9.0.0.0, and the full Wolverine.Http.Tests suite is 631/631 green on net9.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 21, 2026
Closed
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.
Summary
BufferedInMemory()left the row inwolverine_incomingforever, causing it to be reprocessed on every host restart.BufferedLocalQueue.EnqueueDirectlyAsync(the recovery entry point for local queues) used theBufferedReceiveritself as the channel callback. ItsCompleteAsyncis a no-op, so the inbox row was never markedHandled.NodeAgentController.StartLocallythen resetowner_id = 0on the next startup and recovery picked it up again.ListeningAgent.EnqueueDirectlyAsyncfor transports: wrap the recovered envelope in a tinyLocalQueueRecoveryListenerwhoseCompleteAsynccallsMarkIncomingEnvelopeAsHandledAsync, then route throughIReceiver.ReceivedAsyncso the existing_completeBlockfires the wrapper.Scope
.ProcessInline()at all (the mode setter throws), so there is no TCP case to cover.Files
src/Wolverine/Transports/Local/LocalQueueRecoveryListener.cs(new) —IListenerwrapper used only for the recovery path into a non-durable local queue.src/Wolverine/Transports/Local/BufferedLocalQueue.cs— constructor stores the runtime;EnqueueDirectlyAsyncnow dispatches throughIReceiver.ReceivedAsyncwith the wrapper.src/Persistence/PostgresqlTests/Bugs/Bug_1942_replay_dlq_to_buffered_or_inline.cs(new) — two tests: buffered local queue (fails pre-fix, passes post-fix) and RabbitMQ Inline (passes both, locks in RabbitMQ DLQ Replay not triggered #1594).src/Persistence/PostgresqlTests/PostgresqlTests.csproj— addedWolverine.RabbitMQproject reference for the second test.Test plan
dotnet test src/Persistence/PostgresqlTests/PostgresqlTests.csproj --framework net9.0 --filter Bug_1942— both tests passdotnet test src/Persistence/PostgresqlTests/PostgresqlTests.csproj --framework net9.0— 357 / 357 passdotnet test src/Testing/CoreTests/CoreTests.csproj --framework net9.0— 1344 / 1344 passdotnet test src/Transports/RabbitMQ/Wolverine.RabbitMQ.Tests/Wolverine.RabbitMQ.Tests.csproj --framework net9.0 --filter Bug_1594— 3 / 3 pass (RabbitMQ DLQ Replay not triggered #1594 regression locked in)Bug_DLQ_*— 23 / 23 pass🤖 Generated with Claude Code