[#4791] Extract snapshot sourcing composition into its own enhancer - #4792
Merged
laura-devriendt-lemon merged 2 commits intoJul 28, 2026
Merged
Conversation
laura-devriendt-lemon
requested review from
hjohn,
jangalinski and
smcvb
and removed request for
a team
July 28, 2026 08:09
hjohn
reviewed
Jul 28, 2026
hjohn
reviewed
Jul 28, 2026
laura-devriendt-lemon
force-pushed
the
enhancement/4791/extract-snapshot-sourcing-enhancer
branch
from
July 28, 2026 08:57
b9d19c8 to
e0bdc95
Compare
laura-devriendt-lemon
deleted the
enhancement/4791/extract-snapshot-sourcing-enhancer
branch
July 28, 2026 12:09
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 #4791
Supersedes #4788, which added a
handlesSnapshotSourcing()capability method toEventStorageEngine. That approach left two signals meaning "do not decorate this engine" with opposite precedence against a configuredSnapshotStore, and let an engine silently override an explicitly registered one. This replaces it: the identity comparison stays the only signal, and the decision moves to the configuration layer, where a topology can take it over.What changes
The snapshot-composition decorator moves into its own enhancer.
SnapshotSourcingConfigurationEnhancerregisters that one decorator and nothing else.EventSourcingConfigurationDefaultsregisters it during its ownenhance(ComponentRegistry), so applications registering those defaults manually receive it too. It is deliberately not contributed through theServiceLoader, keeping a single registration channel and avoiding a duplicate-registration warning on every module build.The composition rule moves to a static factory.
SnapshotCapableEventStorageEngine.decorate(engine, snapshotStore)returns the engine as is when it is the snapshot store itself, and decorates it otherwise. One home for the rule, reusable by a topology composing per shard, tenant, or region.Disabling the enhancer becomes the supported opt-out. Documented on the enhancer and on the snapshotting page, with the consequence of disabling it without composing elsewhere spelled out: snapshots keep being written while never being read.
Bug fixed along the way
decorate(...)also returns an already-decorated engine as is, which fixes a defect onmain.DefaultComponentRegistry.copyWithDecoratorsAndEnhancers()copies the parent's decorator definitions into a module registry, and that registry re-runs the copied enhancers, registering the same decorator a second time. The identity check passes for the second application, because the already-decorated engine is not the registered store, so a module registering its ownEventStorageEngineandSnapshotStoregot an engine decorated twice.That redundant decoration costs a layer and a stream hop per sourcing, and silently ignores the second decoration's snapshot store. It does not cause a second snapshot read: the outer decoration rewrites the condition to an absolute one before delegating, so the inner one passes it through. A snapshot is therefore still read exactly once, which is why
decoratesAModulesOwnEventStorageEngineExactlyOnceasserts the nesting rather than a load count. I first wrote it as a load-count assertion and it passed with the guard removed, so it would have proven nothing. The structural version fails with the guard removed, and only it fails.The new enhancer carries
@RegistrationScope(scope = CURRENT), so the defaults being copied into module registries do not register it again. Without it, every module build logs aDuplicate Configuration Enhancer registrationwarning: 41 of them in oneeventsourcingtest run.Behaviour
No behaviour change for existing applications, and no
EventStorageEngineAPI added. All ten pre-existingEventSourcingConfigurationDefaultsTestcases pass unchanged, includingdecoratesEventStorageEngineWhenSnapshotStoreIsDifferentInstance_evenIfEngineImplementsSnapshotStore— a separately configuredSnapshotStorestill wins, since implementingSnapshotStorestill does not opt an engine out.The new enhancer is deliberately public rather than
@Internal: its class literal is the opt-out contract, so renaming it in a minor would break callers at compile time.Motivation
A routing event storage engine that fans out to one engine per context, each composed with that context's own snapshot store. Decorating the routing engine consumes the snapshot sourcing strategy before any context is known, so the composed engines never receive it. Tracked internally as
axoniq-framework#263.Testing
SnapshotSourcingConfigurationEnhancerTestcovers order relative to the defaults, decoration with a snapshot store present, the manual-defaults-registration path, the disable opt-out both through the registry and from an earlier-ordered enhancer (the cross-repo path, and the order-sensitive one), single decoration in a module registry, and no decoration without a snapshot store.SnapshotCapableEventStorageEngineTest.Decoratecovers all three factory branches plus the null contracts. Fulleventsourcingmodule: 542 tests, no failures.