Repository navigation
MartenToWolverineOutbox ignores per-store AncillaryMartenIntegration.SchemaName for projection-batch envelope writes #2887
Description
Versions
- WolverineFx 6.0.0
- WolverineFx.Marten 6.0.0
- Marten 9.0.0
- .NET 10
- PostgreSQL 18
Setup (modular monolith, ancillary store on a separate physical database)
// Main store on database "main_db", default DatabaseSchemaName "public"
services.AddMarten(opts =>
{
opts.Connection(mainConn);
}).IntegrateWithWolverine();
// Ancillary store on a different physical database "refs_db"
services.AddMartenStore<IDebtorStore>(opts =>
{
opts.Connection(refsConn);
opts.DatabaseSchemaName = "debtors";
}).IntegrateWithWolverine(x => x.SchemaName = "debtors");An async multi-stream projection registered on IDebtorStore overrides RaiseSideEffects and calls slice.PublishMessage(...).
Reproduction
- Drop both databases (fresh state).
- Boot the host with
AutoCreate.All.pg_tablesconfirms the expected layout:main_db:public.wolverine_*(12 tables: cluster + envelopes)refs_db:debtors.wolverine_*(7 envelope tables)
- Trigger an event that produces a slice for the projection.
- Drive
ForceAllMartenDaemonActivityToCatchUpAsync<IDebtorStore>(or let the daemon process it). - Projection batch fails:
Npgsql.PostgresException: 42P01: relation "public.wolverine_incoming_envelopes" does not exist
at Marten.Internal.Sessions.AutoClosingLifetime.ExecuteBatchPagesAsync
at Marten.Internal.Sessions.DocumentSessionBase.ExecuteBatchAsync
at Marten.Events.Daemon.Internals.ProjectionBatch.ExecuteAsync
at JasperFx.Events.Daemon.GroupedProjectionExecution.applyBatchOperationsToDatabaseAsync
Expected
The outbox write for the IDebtorStore projection goes to debtors.wolverine_outgoing_envelopes on refs_db (or whatever the per-store SchemaName is configured to).
Observed
The outbox write targets public.wolverine_incoming_envelopes on refs_db, ignoring the integration's SchemaName. The schema doesn't exist there → 42P01.
Workaround and second symptom
Setting options.Durability.MessageStorageSchemaName = "debtors" globally on WolverineOptions makes the projection-batch outbox write to debtors.wolverine_* correctly for the ancillary store. But at host shutdown, MessageStoreCollection.ReleaseAllOwnershipAsync for the main store now queries debtors.wolverine_incoming_envelopes on main_db, where the debtors schema does not exist (the main Marten store has DatabaseSchemaName = "public"). With AutoCreate.CreateOrUpdate (typical production setting) the debtors schema is not auto-created in main_db and shutdown crashes:
Npgsql.PostgresException: 42P01: relation "debtors.wolverine_incoming_envelopes" does not exist
at Wolverine.Persistence.MessageStoreCollection.ReleaseAllOwnershipAsync(int nodeNumber)
at Wolverine.Runtime.WolverineRuntime.StopAsync(CancellationToken cancellationToken)
So we're stuck between two failure modes:
- Without the global
MessageStorageSchemaName→ projection-batch outbox writes for the ancillary store ignore the per-storeSchemaNameand targetpublic.*(wrong location). - With the global override → main-store envelope storage targets the override schema in the main DB, which doesn't auto-create with
AutoCreate.CreateOrUpdate.
What I verified
- Envelope tables physically exist in
debtors.wolverine_*onrefs_dbafter startup with per-storeSchemaName = "debtors"(verified viapg_tables). - The failing call comes from
MartenToWolverineOutboxinsideProjectionBatch.ExecuteAsync; the SQL it generates usespublic.wolverine_*. - The full integration test suite (161 tests) on fresh DBs passes with the global override (
AutoCreate.All); 14 of them fail without it on the path throughslice.PublishMessagein an ancillary-store projection. - Reading
AncillaryWolverineOptionsMartenExtensions.cs, schema resolution isintegration.SchemaName ??= runtime.Options.Durability.MessageStorageSchemaName ?? store.Options.DatabaseSchemaName;— the per-store value should win, but the outbox writer evidently consults a different source.
Ask
Either:
- Honour the resolved per-store
SchemaNamein the projection-batchMartenToWolverineOutboxwriter (so the per-store API actually controls envelope writes end-to-end), or - Document a supported configuration for "main + ancillary stores on separate physical databases, each with their own envelope schema".
Happy to put together a minimal repro project if helpful.
Activity
Merge pull request #2888 from JasperFx/fix-2887-ancillary-outbox-schema
Fix ancillary store outbox schema for projection side effects (Jasper…
Add 6.0.1 release notes to CHANGELOG