fix(marten): GH-2669 alternative — Uri-based ancillary store routing (no IMessageStore.Id dependency)#2674
Merged
jeremydmiller merged 1 commit intomainfrom May 4, 2026
Conversation
…s ancillary store GH-2669 alternative to #2670. Same root cause + same primary fix on the DurableLocalQueue / DurableReceiver side (the receiving handler's ancillary store wins over the publisher-stamped envelope.Store), but the gate on FlushOutgoingMessagesOnCommit's in-transaction inbox UPDATE compares stores by Uri rather than IMessageStore.Id. Two reasons to prefer the Uri compare here: 1. The same-database fallback in the envelope.Store==null branch (GH-2382) already uses `mainStore.Uri == _messageStore.Uri`. Reusing that heuristic keeps the file's local notion of "same store" consistent and avoids introducing IMessageStore.Id as a contract surface. 2. PostgresqlMessageStore.Id is `DatabaseId(server, database)` — same DB different schema = same Id. The PR's Id-equality therefore enables a same-database cross-schema in-transaction UPDATE, which only works when the connection user has cross-schema permissions. Uri-equality (server + database + schema) is more conservative: cross-schema cases skip the in-transaction shortcut and let the envelope's owning store mark-handled through its own connection. Same end-state for the ancillary handler scenario in this issue, slightly more predictable behavior in the cross-schema case. Three changes: - DurableLocalQueue.assignAncillaryStoreIfNeeded — drop the `if (envelope.Store != null) return;` guard so the receiving handler's ancillary store overrides any store the publisher stamped. This is the primary fix; without it the publisher's main-store reference reached FlushOutgoingMessagesOnCommit and the envelopeStore.IncomingFullName branch pointed at a table that doesn't exist in the ancillary database (`42P01: relation "public.wolverine_incoming_envelopes" does not exist`). - DurableReceiver.assignAncillaryStoreIfNeeded — symmetric guard removal for the broker-receiver path. - Wolverine.Marten/FlushOutgoingMessagesOnCommit.BeforeSaveChangesAsync — in the Ancillary listener, when envelope.Store is set, only fold the handled-update into the current Marten transaction if envelopeStore.Uri matches _messageStore.Uri (same connection / schema). Cross-store envelopes return early; the envelope's owning store handles the mark-handled separately. Test: PolicyTests adopted from #2670 verbatim (Bug_2669_ancillary_marten_store_local_message_from_main_store.cs). Verified bug-present (this fix reverted) reproduces the exact `42P01: relation "public.wolverine_incoming_envelopes" does not exist` error from the issue; with the fix the test passes. 34/34 of the wider Marten ancillary / Bug_2382 / Bug_2576 / Bug_2669 subset pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
jeremydmiller
added a commit
that referenced
this pull request
May 4, 2026
Minor release. Highlights: - WolverineFx.Marten: durable local messages routed by the receiving handler's ancillary store (Uri-based gate, no IMessageStore.Id dependency). Closes #2669. PR #2674. - WolverineFx.RabbitMQ: public AddClusterNode(...) API for multi-node failover. Closes #2659. PR #2664. - WolverineFx.Polecat: fixed NRE in OutboxedSessionFactory when constructing the FlushOutgoingMessagesOnCommit listener. Closes #2668. PR #2672. - WolverineFx core: new DocumentStores collection on ServiceCapabilities for CritterWatch document-side rendering. - Dependency bumps: JasperFx 1.28.2 → 1.29.0, JasperFx.Events 1.31.1 → 1.33.1, Marten + Marten.AspNetCore 8.32.0 → 8.35.0. Also backfilled a 5.36.2 entry in CHANGELOG covering the EF Core + outbox flush rework from PR #2665 that landed without a CHANGELOG note at the time. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 4, 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.
Alternative to #2670. Same root cause + same primary fix on the
DurableLocalQueue/DurableReceiverside (the receiving handler's ancillary store wins over the publisher-stampedenvelope.Store), but the gate onFlushOutgoingMessagesOnCommit's in-transaction inboxUPDATEcompares stores byUrirather thanIMessageStore.Id.Why Uri instead of Id
Internal consistency: the same-database fallback in the
envelope.Store == nullbranch (Durable receiver should store envelopes in the ancillary store's database/schema when handler targets an ancillary MartenStore #2382) atFlushOutgoingMessagesOnCommit.cs:51-52already usesmainStore.Uri == _messageStore.Uri. Reusing that heuristic keeps the file's local notion of "same store" consistent and avoids introducingIMessageStore.Idas a contract surface.Conservative cross-schema semantics:
PostgresqlMessageStore.IdisDatabaseId(server, database)— same DB different schema = same Id. The PR's Id-equality therefore enables a same-database cross-schema in-transactionUPDATE, which only works when the connection user has cross-schema permissions.Uri-equality (server + database + schema) is more conservative — cross-schema cases skip the in-transaction shortcut and let the envelope's owning store mark-handled through its own connection. Same end-state for the ancillary handler scenario in this issue, slightly more predictable behavior in the cross-schema case.Three changes
DurableLocalQueue.assignAncillaryStoreIfNeeded— drop theif (envelope.Store != null) return;guard so the receiving handler's ancillary store overrides any store the publisher stamped. This is the primary fix; without it the publisher's main-store reference reachedFlushOutgoingMessagesOnCommitand theenvelopeStore.IncomingFullNamebranch pointed at a table that doesn't exist in the ancillary database (42P01: relation \"public.wolverine_incoming_envelopes\" does not exist).DurableReceiver.assignAncillaryStoreIfNeeded— symmetric guard removal for the broker-receiver path.Wolverine.Marten/FlushOutgoingMessagesOnCommit.BeforeSaveChangesAsync— in theAncillarylistener, whenenvelope.Storeis set, only fold the handled-update into the current Marten transaction ifenvelopeStore.Urimatches_messageStore.Uri(same connection / schema). Cross-store envelopes return early; the envelope's owning store handles the mark-handled separately.Tests
Adopted #2670's regression test verbatim (
Bug_2669_ancillary_marten_store_local_message_from_main_store.cs).42P01: relation \"public.wolverine_incoming_envelopes\" does not existerror from the issue.Bug_2382/Bug_2576/Bug_2669subset pass with the fix.Test plan
martenworkflow green.NETworkflow greenCloses #2669. Alternative to #2670 — pick whichever resolution path you prefer; either resolves the underlying bug.
🤖 Generated with Claude Code