Repository navigation
feat: Add the override layer, the store overlay, and the override source configuration - #222
Draft
kinyoklion wants to merge 5 commits into
Draft
kinyoklion wants to merge 5 commits into
kinyoklion wants to merge 5 commits into
Conversation
kinyoklion
force-pushed
the
rlamb/overrides-java-model-marker
branch
from
September 28, 2026 21:03
39b0b95 to
d82beda
Compare
kinyoklion
force-pushed
the
rlamb/overrides-java-override-layer
branch
from
September 28, 2026 21:04
3ddbca0 to
51b497a
Compare
kinyoklion
force-pushed
the
rlamb/overrides-java-model-marker
branch
from
October 1, 2026 23:43
d82beda to
1ba722a
Compare
kinyoklion
force-pushed
the
rlamb/overrides-java-override-layer
branch
from
October 1, 2026 23:44
51b497a to
3e12570
Compare
kinyoklion
force-pushed
the
rlamb/overrides-java-model-marker
branch
from
October 3, 2026 00:58
1ba722a to
996b8d3
Compare
kinyoklion
force-pushed
the
rlamb/overrides-java-override-layer
branch
from
October 3, 2026 00:58
3e12570 to
2d12762
Compare
Member
Author
|
bugbot review |
…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.
kinyoklion
force-pushed
the
rlamb/overrides-java-model-marker
branch
from
October 13, 2026 21:02
996b8d3 to
ca22bc0
Compare
kinyoklion
force-pushed
the
rlamb/overrides-java-override-layer
branch
from
October 13, 2026 21:02
e58a4f7 to
ee0d2be
Compare
…n changes, and let isFlagKnown see overrides A flag override can reference LaunchDarkly data: a rule that matches a LaunchDarkly segment, or a prerequisite that is a LaunchDarkly flag. DataSourceUpdatesImpl computes flag change notifications from a dependency tracker built from LaunchDarkly data alone, so when such a segment or flag changed, the flags whose overrides depend on it were not notified. The override layer now keeps the dependency edges of its own entries, rebuilt on every replacement, and the data source updates ask it for the override entries that depend on a changed item. The chain can cross in both directions, so each newly reached item is expanded through both sets of edges until nothing new is found. isFlagKnown consults the override layer before the not-initialized short circuit, the same as evaluation does, so a caller that gates on it does not skip an override during an outage.
Member
Author
|
bugbot review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 5a27c9f. Configure here.
This branch has not been deployed
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
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
Follow-ups from review
DataSourceUpdatesImplexpands each changed LaunchDarkly item through both its own tracker and the layer's until nothing new is reached, so an override that matches a LaunchDarkly segment or has a LaunchDarkly prerequisite is notified like the LaunchDarkly flags that depend on it.isFlagKnownconsults the override layer before the not-initialized short circuit, the same as evaluation does, so an override-only flag is known while the client is uninitialized.Note
Overview
Adds an experimental override layer for the FDv2 data system so operators can supply full flag/segment snapshots that take precedence over LaunchDarkly data at the store read boundary, without affecting client initialization or data source status.
Configuration & lifecycle:
DataSystemBuilder.overrides(...)wires anOverrideSourcethat pushes wholesale snapshots toOverrideSink. FDv2 builds the source like other components, starts it before the data source (so sync loads apply during construction), overlays reads viaOverrideOverlayStore, and closes the source with the client. Offline clients skip overrides; FDv1 always returns a null override layer.Evaluation & API behavior: Overridden flags evaluate even when the client/store is not initialized; non-overridden flags still get the client-not-ready default.
isFlagKnownandallFlagsStatefollow the same rules (override-only all-flags when uninitialized, with a one-time warning). Override-affected results keep value/reason but disable per-flag event tracking inFeatureFlagsState.Change notifications: LaunchDarkly updates fan out through both the normal dependency tracker and the override layer’s edges (
ExternalDependencies), so LD segment/prerequisite changes notify overridden dependents; override updates use merged-view diffs and dependency fan-out inOverrideSinkImpl.Reviewed by Cursor Bugbot for commit 5a27c9f. Bugbot is set up for automated code reviews on this repo. Configure here.