Repository navigation
Keep the correlation and causation on an event's clones (#1101) - #1107
Merged
Merged
Conversation
CloneEventWithNewData and WithData dropped CorrelationId and CausationId, so a slice built by fan out or ReplaceEvent held nulls and PublishMessage(message) fell back to the tenant-only call that #1105 fixed for every other slice. There are two clone methods and two FanOut extensions. A slicer's fan out rules and ReplaceEvent use CloneEventWithNewData, while EventSlice.FanOut, and so SliceGroup.FanOutOnEach, uses WithData. WithData now delegates so the two cannot drift apart again. No store writes these ids from an event: Marten and Fisher read only Version and Sequence from the last event, Polecat reads nothing, and the document columns and ITracked come from the session. A projection that reads them off a clone itself now sees the source's value instead of null. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 2, 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.
Follow-up to #1105 for #1101.
#1105 made
slice.PublishMessage(message)carry the correlation id that all of a slice's events agree on, and treats a null on an appended event as no agreement. The methods that clone an event droppedCorrelationIdandCausationId, so any slice built by fan out orReplaceEventheld nulls and quietly kept the original bug.There were two clones, not one
CloneEventWithNewDataFanOutOperator→JasperFx.Events.Grouping.EventListExtensions.FanOut), bothReplaceEventoverloadsWithDataEventSlice.FanOut(→JasperFx.Events.EventListExtensions.FanOut), soSliceGroup.FanOutOnEach, and custom groupers, as in Marten's sampleThe two had identical bodies, as do the two
FanOutextensions.CloneEventWithNewDatanow copies both ids, andWithDatadelegates to it so they cannot drift apart again.Behaviour change
Nothing a store writes changes. This is from reading Marten 2f2769bca, Polecat 85452490c, Fisher 3f4c4bebd and Weasel at their heads. None of it was run against a store.
StoreProjectionreads from the last eventVersion,SequenceVersion,SequenceStoreProjectionreadscorrelation_id/causation_id,ITrackedNo store sets a session's ids from an event, and no store overrides
ApplyMetadata. Clones are never persisted either, becauseBuildOperationswrites onlyRaisedEvents, which are new events.What does change:
PublishMessage(message)on a fan out or enriched slice whose events agreeMessageMetadata.From(clone)CorrelationId/CausationIdoff a clone inApply,Create,ShouldDelete,EvolveorApplyMetadataThe last row can change stored document bodies, but only in projections that copy these ids onto their own documents, which is what Marten documents
ApplyMetadatafor. Todaydoc.X = e.CorrelationIdinApplyMetadataends on null whenever a clone is last in the slice, because the hook runs for every event. Documents already stored keep their value until their next event or a rebuild.Causation stays off messages: neither
slice.PublishMessagenorMessageMetadata.Fromcopies it, and a test pins that for clones. Slices with two flows still fall back to the tenant, since a clone only repeats its source's id and so can never break or create a false agreement.Not changed
UserName,IsArchived,IsSkipped,AggregateTypeName,EventTypeName,DotNetTypeNameand tags are still not copied, so both doc comments still overclaim.UserNamewould need the same trace, sinceApplyMetadataexists for "LastModifiedBy".Testing
./build.sh Test --root "$PWD": every target succeeded, on net9.0 and net10.0 where the project targets both.EventTests1735 passed,EventStoreTests350 passed with 23 skipped.EventExtensions.csandEventTypeExtensions.csback atmain, 14EventTestscases fail, including fan out through bothFanOutextensions,ReplaceEvent, the async runner and the inline path in both fan out modes. Two guard cases for slices that don't agree pass either way, as they should.🤖 Generated with Claude Code