Marten batch: JasperFx.Events 1.29.0 + GH-4267 + GH-4268 reproducer attempt#4269
Merged
jeremydmiller merged 3 commits intomasterfrom Apr 21, 2026
Merged
Conversation
## JasperFx.Events 1.29.0 plumbing (supports wolverine#2545) - Bump Directory.Packages.props: JasperFx.Events 1.28.1 → 1.29.0 and JasperFx 1.24.1 → 1.26.0 (transitive floor from the 1.29.0 bump). - ProjectionBatch.PublishMessageAsync(object, MessageMetadata) overload forwarding to the underlying IMessageSink with full metadata, so downstream sinks (e.g. Wolverine) can map the metadata onto their native delivery options (correlation id, causation id, headers, user name). ## GH-4267 — AddProjectionWithServices on IServiceCollection AddProjectionWithServices was only available on MartenConfigurationExpression / MartenStoreExpression<T>, which means callers need the builder chain returned by AddMarten(). In modular-monolith setups the builder is consumed once at composition root and individual modules only have IServiceCollection, forcing users into the undiscoverable TProjection.Register<TProjection>(services, ...) JasperFx internal. Adds two new extensions on IServiceCollection mirroring the builder versions exactly: services.AddProjectionWithServices<TProjection>( lifecycle, lifetime, configure); services.AddProjectionWithServices<TProjection, TStore>( lifecycle, lifetime, configure); Both are thin wrappers over TProjection.Register<...>(services, ...). Covered by two new integration tests (ContainerScopedProjectionTests/projections_registered_directly_on_IServiceCollection) that exercise the default-store and ancillary-store paths end-to-end against Postgres. ## GH-4268 — reproducer attempt, does not reproduce The reporter flipped three async projections to inline and hit a migration exception involving mt_doc_envelope (Wolverine's outbox table) and partitioned-table constraint handling. This change adds the closest approximation I can build from the reported setup: - Conjoined tenancy (events + all documents) - UseArchivedStreamPartitioning - Envelope metadata + Headers + Version enabled on all docs - EnableSideEffectsOnInlineProjections - TenantIdStyle.ForceLowerCase - Advanced.DefaultTenantUsageEnabled = false - AppendMode = Quick - Store A creates the schema + commits one event with the projection as Async - Store B points at the same schema with the projection flipped to Inline and then commits another event Result: test passes cleanly. The reporter's error is almost certainly tied to Wolverine-specific envelope integration that is outside Marten, and cannot be reproduced from Marten alone without more information. Left in place as a baseline regression guard so this specific direct async → inline flip on a conjoined + partitioned schema stays green going forward. The test docstring spells out what we did and did not reproduce. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up attempt: instead of letting SaveChanges lazily materialize only the per-document storage, explicitly call ApplyAllConfiguredChangesToDatabaseAsync on the separate store after flipping to Inline. That makes Weasel diff the full model against the existing schema — the widest possible surface the reporter's running app could hit on the first deploy. Result: still passes. The async → inline flip on a conjoined + partitioned schema with every relevant option toggled does not trigger the "unique constraint on partitioned table must include all partitioning columns" failure, even when the full migration is forced. This strengthens the earlier finding that the async → inline flip alone isn't the driver. Something else in the reporter's deployment history is making Marten's model describe the mt_doc_envelope user document as single-tenant while the live table is conjoined + partitioned, and we can't synthesize that condition without more information. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merged
2 tasks
jeremydmiller
added a commit
to JasperFx/weasel
that referenced
this pull request
Apr 21, 2026
…I break Upgrades the Weasel graph onto the latest JasperFx (1.24.0 → 1.26.0), which transitively upgrades Spectre.Console from 0.53 to 0.55. Spectre.Console 0.55 removed the StringExtensions.EscapeMarkup(string) extension (the static Spectre.Console.Markup.Escape(string) is the replacement). Weasel.Core.CommandLine.AssertCommand used the old extension form in two places, which surfaced downstream in Marten as a MissingMethodException on every db-assert / resources command. Patched AssertCommand to call Markup.Escape directly. Unblocks JasperFx/marten#4269. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CoreTests.jasper_fx_mechanics.* were failing on PR CI with:
System.MissingMethodException: Method not found:
'System.String Spectre.Console.StringExtensions.EscapeMarkup(System.String)'
at Weasel.Core.CommandLine.AssertCommand.Execute(WeaselInput input)
Root cause: Spectre.Console 0.55 (pulled in transitively by the
JasperFx 1.26 bump earlier on this branch) removed the
StringExtensions.EscapeMarkup(string) extension. The replacement
Spectre.Console.Markup.Escape(string) is the supported API. Weasel
8.14.1 switches to it; see JasperFx/weasel#252.
Local CoreTests.jasper_fx_mechanics subset: 13/13 pass (was 6/13).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
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
Batched work against Marten's
master:Directory.Packages.propsbumps, plus a newProjectionBatch.PublishMessageAsync(object, MessageMetadata)overload that forwards to theIMessageSinkmetadata path. Enables the Wolverine-side work in JasperFx/wolverine#2545.AddProjectionWithServicesusable without builder chain #4267 —AddProjectionWithServicesextensions onIServiceCollection, so modular-monolith modules that only haveIServiceCollectioncan register projections without reaching for undiscoverable JasperFx internals.1. JasperFx.Events 1.29.0
JasperFx.Events1.28.1 → 1.29.0JasperFx1.24.1 → 1.26.0 (transitive floor required by 1.29.0)Marten.Events.Daemon.Internals.ProjectionBatch: newPublishMessageAsync(object message, MessageMetadata metadata)overload that callsIMessageSink.PublishAsync(message, metadata), letting downstream sinks (Wolverine) map metadata to their native delivery options.2. GH-4267 —
AddProjectionWithServicesonIServiceCollectionCloses #4267.
Thin wrappers over
TProjection.Register<…>(services, …). Covered by two new integration tests inContainerScopedProjectionTests/projections_registered_directly_on_IServiceCollection.cs— both paths (default store and ancillaryAddMartenStore<T>) green end-to-end against Postgres.3. GH-4268 — reproducer attempt
The reporter flipped three async projections to inline and hit a migration exception:
unique constraint on partitioned table must include all partitioning columnswhile Weasel was droppingtenant_idfrommt_doc_envelope(Wolverine's outbox table).Added the closest approximation of the reporter's setup (conjoined tenancy + archived-stream partitioning + envelope metadata enabled +
EnableSideEffectsOnInlineProjections+TenantIdStyle.ForceLowerCase+AppendMode.Quick+ store A async → store B inline on the same schema).Result: test passes. The reporter's failure almost certainly involves Wolverine-specific outbox integration that can't be exercised from Marten alone. More information needed from the reporter — not enough here to repro. Leaving the test in as a regression guard.
Test plan
dotnet test src/EventSourcingTests --filter "FullyQualifiedName~SideEffect|FullyQualifiedName~publish_message|FullyQualifiedName~Projection"— 223/223 pass (post-1.29.0 bump)dotnet test src/ContainerScopedProjectionTests --filter "projections_registered_directly_on_IServiceCollection"— 2/2 passdotnet test --filter "Bug_4268"— 1/1 pass (baseline regression)Downstream
Once this merges and Marten releases, JasperFx/wolverine#2545 can land its
MartenToWolverineMessageBatchmetadata-aware overload and close the user-facing story.🤖 Generated with Claude Code