Skip to content

Fix ancillary Marten store routing for durable local messages#2670

Closed
erdtsieck wants to merge 1 commit intoJasperFx:mainfrom
erdtsieck:feature/2669-ancillary-marten-store-routing
Closed

Fix ancillary Marten store routing for durable local messages#2670
erdtsieck wants to merge 1 commit intoJasperFx:mainfrom
erdtsieck:feature/2669-ancillary-marten-store-routing

Conversation

@erdtsieck
Copy link
Copy Markdown
Contributor

Closes #2669.

Summary

  • Added a Marten regression test for a durable local message published from the main store and handled by an ancillary Marten store.
  • Let the receiving handler's ancillary store association override the publishing context store for durable local queues and durable receivers.
  • Avoid folding the handled-inbox update into an ancillary Marten transaction when the envelope belongs to a different PostgreSQL message store.

Root Cause

Local durable envelopes published from a main-store handler could keep the main message store on Envelope.Store. When the receiving handler used [MartenStore(typeof(...))] for an ancillary Marten store, the handler session still attempted to mark the envelope handled in the main/public inbox table, which can fail when that table only exists in the ancillary schema/database.

Testing

  • dotnet test src\Persistence\MartenTests\MartenTests.csproj --filter FullyQualifiedName~Bug_ancillary_marten_store_local_message_from_main_store.durable_local_message_from_main_store_can_be_handled_by_ancillary_marten_store
  • dotnet test src\Persistence\MartenTests\MartenTests.csproj -f net9.0 --filter FullyQualifiedName~Bug_2576_ancillary_scheduled_message_stuck_incoming --no-restore --no-build

@erdtsieck erdtsieck force-pushed the feature/2669-ancillary-marten-store-routing branch from 43b2478 to 5e44170 Compare May 4, 2026 09:13
@erdtsieck erdtsieck marked this pull request as ready for review May 4, 2026 09:13
jeremydmiller added a commit that referenced this pull request May 4, 2026
…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>
@erdtsieck erdtsieck closed this May 6, 2026
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.

Durable local messages from the main Marten store can use the wrong inbox for ancillary store handlers

1 participant