[#4797] Fall back to the event identifier when a sequencing policy resolves nothing - #4818
Open
schananas wants to merge 1 commit into
Open
[#4797] Fall back to the event identifier when a sequencing policy resolves nothing#4818schananas wants to merge 1 commit into
schananas wants to merge 1 commit into
Conversation
schananas
requested review from
hatzlj,
laura-devriendt-lemon and
zambrovski
and removed request for
a team
July 29, 2026 15:36
schananas
force-pushed
the
bug/4797/sequence-identifier-optional-fallback
branch
from
July 29, 2026 17:46
967be50 to
eb38968
Compare
…solves nothing `SequencingPolicy.sequenceIdentifierFor` documents an empty result as normal, but `SimpleEventHandlingComponent` unwrapped it with `Optional.get()`. `NoOpSequencingPolicy`, and `SequentialPerAggregatePolicy` on any store that does not set the legacy aggregate identifier, therefore threw `NoSuchElementException` on every event and the processor delivered nothing at all, for ever, with no configuration error to point at it. Both call sites now fall back to the event identifier, which is what `SegmentMatcher` already does, so "no sequencing" means full concurrency instead of a total read-side stall. Trading the abort for a fallback trades a loud failure for a silent downgrade: a projection that asked for per-aggregate ordering now gets none, and nothing said so. The component therefore warns on the first fallback, naming the policy and the component. Once per component, not once per event, as this sits on the per-event path and an event-rate warning would be its own defect. The wired default, `HierarchicalSequencingPolicy(SequentialPerAggregatePolicy, SequentialPolicy)`, always resolves through its secondary policy, so the warning cannot fire for the default configuration. `NoOpSequencingPolicy` is exempt from the warning. An empty result there is the user explicitly declaring that no sequencing is wanted, not a degradation, and a warning that fires on correct configuration is the kind that gets filtered out wholesale, burying the case it exists for. `MessagingConfigurationDefaults` already recognises this policy by type for the same reason, so the exception is carved at the log site rather than by widening the `SequencingPolicy` interface for it. Fixes #4797
schananas
force-pushed
the
bug/4797/sequence-identifier-optional-fallback
branch
from
July 29, 2026 17:55
eb38968 to
97db32a
Compare
MateuszNaKodach
self-requested a review
July 31, 2026 12:46
MateuszNaKodach
approved these changes
Jul 31, 2026
MateuszNaKodach
left a comment
Contributor
There was a problem hiding this comment.
Good finding! Wow. I think we can merge that :)
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.
Fixes #4797
What changed
SequencingPolicy.sequenceIdentifierFordocuments an empty result as normal, butSimpleEventHandlingComponentunwrapped it withOptional.get(), under a@SuppressWarnings("OptionalGetWithoutIsPresent"). Both call sites now fall back to the event identifier.Two shipped policies legitimately answer empty, so they threw
NoSuchElementExceptionon every event and the processor delivered nothing at all, for ever:NoOpSequencingPolicy, andSequentialPerAggregatePolicyon any store that does not set the legacy aggregate identifier.The old behaviour was not silent:
DefaultWorkPackageEventFiltercatches,PropagatingErrorHandlerrethrows, andWorkPackage.canHandle:321logs"Error while detecting whether event can be handled in Work Package ... Aborting Work Package"with theOptional.getstack trace, then aborts. The permanent stall is the defect; the silence is not.Why this fallback
FullConcurrencyPolicyalready returnsOptional.of(message.identifier()). A per-event identifier is how the framework encodes "no sequencing", so falling back to it makes an emptyOptionalmean the same thing as the policy that explicitly asks for no sequencing.A warning when the fallback fires
SimpleEventHandlingComponentnow warns once per component, naming the policy class, when a policy resolves no identifier and the fallback is used. Without it, explicitly configuringSequentialPerAggregatePolicyon a DCB store goes from a loud abort to silently getting one sequence per event -- the user asked for per-aggregate ordering and gets none.NoOpSequencingPolicyis exempt: an empty answer there is the user's deliberate declaration that they want no sequencing, and a warning that fires on correct configuration gets muted wholesale, burying the case it exists for. Recognising it by type followsMessagingConfigurationDefaults:345, which already does!(commandSequencingPolicy instanceof NoOpSequencingPolicy)for the same reason, andNoOpSequencingPolicy's own Javadoc sanctions it: "Infrastructure components may decide upon this sequencing policy being present bypassing sequencing infrastructure at all."The wired default never returns empty, so it never warns -- asserted by a test, not just reasoned.
Tests
PolicyCannotDetermineAnIdentifiercovers all three branches, including a plain-EventHandlercomponent, the shape a real annotated projection takes.FallbackToEventIdentifierIsLoggedcovers the warning: fires once forSequentialPerAggregatePolicywith no aggregate identifier, silent forNoOpSequencingPolicy, silent for the wired default.Against the true pre-fix production file: 6 errors, all
NoSuchElementException: No value present. The default-policy test correctly stays green in both directions, as a regression guard should.One detail worth knowing if anyone tries to shrink the diff: the
orElsetoorElseGetchange is load-bearing on its own.Optional.orElseevaluates its argument eagerly, so the old code threw even when a nested component had already answered.Targeted run, no failures:
SimpleEventHandlingComponentSequencingPolicyTestSimpleEventHandlingComponentTestSegmentMatcherTestSequentialPerAggregatePolicyTestNo existing test needed updating; nothing had encoded the old throwing behaviour.
For the reviewer
The behaviour change worth a second opinion: per-event identifiers mean "no sequencing", so events under
NoOpSequencingPolicymay now be handled concurrently and spread across segments, where before they were handled not at all. A total read-side stall is not a contract worth preserving, but the chosen default is a judgement call.This does not fix the separate problem that
SequentialPerAggregatePolicyresolves nothing on a DCB store. With this change it degrades quietly to per-event identifiers instead of throwing. That degradation is #4803.Merge note
bug/4803/default-sequencing-policy-dcb-degradation-docstouches the same two files in different regions. Both derive from the same base blobs offorigin/main, so no conflict is expected, but merge one before rebasing the other.