feat: Add the override layer, the store overlay, and the override source configuration - #222
kinyoklion wants to merge 4 commits into
Conversation
39b0b95 to
d82beda
Compare
3ddbca0 to
51b497a
Compare
d82beda to
1ba722a
Compare
51b497a to
3e12570
Compare
…rce configuration The OVERRIDE specification defines an override layer: a runtime-mutable collection of flag and segment definitions, supplied by an override source as complete snapshots, that takes precedence over LaunchDarkly data on a per-key basis at the store read boundary. Overrides are not a data source. They have no effect on initialization status, data availability, or data source status, and they are never persisted. Public surface, all experimental and subject to change: DataSystemBuilder.overrides(ComponentConfigurer<OverrideSource>), DataSystemConfiguration.getOverrideSource(), and the subsystems.OverrideSource and subsystems.OverrideSink interfaces. The SDK builds the source like any other component, starts it before the data source so its initial load completes during client construction, and closes it with the client. An offline client starts no override source. A source that cannot be built fails client construction. OverrideLayer holds marked shallow copies in an immutable map swapped on each update. OverrideOverlayStore implements the read boundary with override precedence for per-key reads and enumeration, and serves the layer alone when the base store fails. OverrideSinkImpl serializes snapshot application and fires the normal flag change notifications for every flag whose merged-view evaluation may have changed. The not-initialized short-circuit consults the layer first, so a flag that the layer holds is served before the client has LaunchDarkly data. The all-flags state does the same and presents an override-affected flag with event tracking off. The specification's test vectors run as a unit test through the full client stack.
1ba722a to
996b8d3
Compare
3e12570 to
2d12762
Compare
|
bugbot review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2d12762. Configure here.
|
|
||
| EvaluatorInterface evaluator = new InputValidatingEvaluator(this.dataSystem.getStore(), bigSegmentStoreWrapper, eventProcessor, evaluationLogger); | ||
| EvaluatorInterface evaluator = new InputValidatingEvaluator(this.dataSystem.getStore(), | ||
| this.dataSystem.getOverrideLayer(), bigSegmentStoreWrapper, eventProcessor, evaluationLogger); |
There was a problem hiding this comment.
isFlagKnown ignores uninitialized overrides
Medium Severity
isFlagKnown still returns false as soon as the store is uninitialized, so an override-only flag that evaluate and allFlagsState already serve in that state is reported as unknown. Callers that gate on isFlagKnown will skip the override during the incident path this feature is meant to cover.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 2d12762. Configure here.
| this.overrideLayer = new OverrideLayer(); | ||
| this.overrideSink = new OverrideSinkImpl(overrideLayer, baseStore, flagChangeBroadcaster, | ||
| logger.subLogger(Loggers.DATA_SOURCE_LOGGER_NAME)); | ||
| this.readOnlyStore = new OverrideOverlayStore(baseStore, overrideLayer); |
There was a problem hiding this comment.
LD updates miss override dependents
Medium Severity
OverrideSinkImpl fans out change notifications over the merged view, but LaunchDarkly store updates still use only base-store dependencies. An override that references a LaunchDarkly segment or prerequisite will not notify that flag when the underlying item changes, so listeners can miss a real evaluation change.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 2d12762. Configure here.


Summary
This adds the override layer that the OVERRIDE specification describes: an override source supplies complete snapshots of flag and segment definitions to an override store, and an overlay at the store read boundary returns the store's entry for a key in preference to LaunchDarkly data. Evaluation, prerequisite and segment resolution, and the all-flags state all read through the overlay, so an override is a full definition that evaluates like any other. Overrides do not take part in the data system: they have no effect on initialization status, data availability, or data source status, and they are never written to a persistent store.
Public surface, all marked experimental and subject to change:
DataSystemBuilder.overrides(ComponentConfigurer<OverrideSource>)andDataSystemConfiguration.getOverrideSource(). The option lives on the FDv2 data system builder only. An FDv1 data source configuration has no place to supply one.subsystems.OverrideSource(start with a sink, close) andsubsystems.OverrideSink(replace the whole layer with one snapshot). The SDK builds the source like any other component, starts it before the data source so its initial load completes during client construction, and closes it with the client. An offline client starts no override source. A source that cannot be built fails client construction the same way other invalid component configuration does.Internals:
OverrideLayerholds marked shallow copies of the supplied entities in an immutable map that is swapped on each update. The source's objects are never modified.OverrideOverlayStoreimplements the read boundary: per-key reads prefer the layer, enumeration is the union with override precedence, and when the base store fails while the layer holds entries the layer's entries are still served.OverrideSinkImplserializes snapshot application and fires the normal flag change notifications for every flag whose merged-view evaluation may have changed, using the dependency tracker over both the old and the new merged views so prerequisite and segment dependents are included.trackEventsandtrackReasonfalse and nodebugEventsUntilDate.The OVERRIDE specification's test vectors run as a unit test through the full client stack (value, variation index, and reason). The per-evaluation summary marker in the vectors is asserted by the events change that follows.
This PR depends on the model and evaluator marking change (rlamb/overrides-java-model-marker) and is based on that branch; retarget it to feat/overrides once that branch merges.
The existing file data source keeps its current behavior; this change does not touch its code paths.
SDK-3246
Note
Overview
Adds experimental flag/segment overrides for the FDv2 data system: operators can configure an
OverrideSourceviaDataSystemBuilder.overrides()that pushes full snapshots into an in-memory override layer taking precedence over LaunchDarkly data at the store read boundary.FDv2 builds optional
OverrideLayer+OverrideOverlayStore+OverrideSinkImpl, starts the override source before the data source (skipped when offline), and closes it with the client. Overrides do not affect initialization or data-source status; FDv1 exposes a null override layer.Evaluation behavior changes:
InputValidatingEvaluatorserves overridden flags even when the client is not initialized, andallFlagsState()can return a valid snapshot of override-only flags (with a one-time warning). Override-affected evaluations keep values/reasons butFeatureFlagsStateturns off per-flag event tracking fields. Flag change listeners fire on override updates with dependency fan-out (prereqs/segments).Public APIs:
OverrideSource,OverrideSink, andDataSystemConfiguration.getOverrideSource(). OVERRIDE spec test vectors and broad unit/integration tests are included.Reviewed by Cursor Bugbot for commit 2d12762. Bugbot is set up for automated code reviews on this repo. Configure here.