Exempt Wolverine's Envelope from blanket multi-tenant doc policies (supersedes #2566 / marten#4268)#2570
Merged
jeremydmiller merged 2 commits intomainfrom Apr 22, 2026
Merged
Conversation
…ten#4268)
Wolverine's Envelope outbox document was silently picking up blanket
document policies applied to a store — most painfully
options.Policies.AllDocumentsAreMultiTenantedWithPartitioning. That
turned mt_doc_envelope into a hash-partitioned, conjoined-tenant table.
When a second store sharing the same schema described Envelope as
single-tenant (its normal default, since Wolverine never asks for
multi-tenancy on its own operational table), Marten's schema diff
emitted an impossible delta:
drop constraint pkey_mt_doc_envelope_id_tenant_id CASCADE;
add CONSTRAINT pkey_mt_doc_envelope_id PRIMARY KEY (id); -- 0A000
drop column tenant_id;
Postgres rejects the ADD PRIMARY KEY on a still-partitioned table with
"unique constraint on partitioned table must include all partitioning
columns". Async projections hid the conflict; inline projections surface
it on the first SaveChanges.
MartenOverrides.Configure now pins Envelope as single-tenant /
unpartitioned via Schema.For<Envelope>().SingleTenanted().DoNotPartition().
Those alterations land on the DocumentMappingBuilder<T> _alterations
list, which fires AFTER applyPolicies + applyPostPolicies during
DocumentMapping construction, so blanket ForAllDocuments policies can't
override them.
Updated the reproducer from #2566 to assert the new guarantee: Envelope
is NOT tenant-partitioned after the async-projection store runs, and the
subsequent inline-side-effects store does not throw
MartenSchemaException on its first SaveChanges.
Verification:
- Bug_4268 reproducer: 1/1 pass (was failing pre-fix)
- MartenTests subset (Bugs + MartenOutbox + publish_messages + AncillaryStores): 52/52 pass
- Wolverine CoreTests: 1346/1346 pass
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 23, 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.
Supersedes #2566. Same change set, opened against the JasperFx repo so it can be merged directly. Closes #2566 once this lands.
Original reproducer authored by @erdtsieck (commit preserved with their authorship). Many thanks for the targeted repro — that's what made the root cause obvious.
The bug
Wolverine's internal
Envelopeoutbox document silently inherited blanket Marten document policies (AllDocumentsAreMultiTenanted/AllDocumentsAreMultiTenantedWithPartitioning). When two stores share a database schema and one applies such a policy:mt_doc_envelopeas conjoined + hash-partitioned bytenant_id.Envelopeas single-tenant. Its firstSaveChangesAsynctriggers a Marten schema diff that emits an impossible migration:Postgres rejects the
ADD PRIMARY KEY (id)step on a still-partitioned table with0A000: unique constraint on partitioned table must include all partitioning columns— exactly the error from marten#4268. Async projections hide the conflict; flipping to inline +EnableSideEffectsOnInlineProjectionssurfaces it on the first append.The fix
In
MartenOverrides.Configure:The
_alterationsregistered bySchema.For<T>()run on theDocumentMappingBuilder<T>afterapplyPoliciesandapplyPostPoliciesduringDocumentMappingconstruction, so blanketForAllDocumentspolicies cannot override them. Marten itself uses the same pattern internally to exemptDeadLetterEvent.We considered an
IDocumentPolicyapproach (lazier — wouldn't pre-registerEnvelopeat all). It loses ordering:Policies.OnDocuments(...)alwaysInsert(0)s into_policies, so a later-registered blanket policy overwrites the per-type fix. Schema-builder alterations are the only public Marten hook that consistently wins. No Marten changes needed for this PR.Verification
marten#4268error)Bugs+MartenOutbox+publish_messages+AncillaryStores)CoreTests🤖 Generated with Claude Code