Repository navigation
feat: Add the override layer, the store overlay, and the override source configuration - #222
Draft
kinyoklion wants to merge 9 commits into
Draft
kinyoklion wants to merge 9 commits into
kinyoklion wants to merge 9 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 |
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
Member
Author
|
bugbot review |
kinyoklion
force-pushed
the
rlamb/overrides-java-override-layer
branch
from
October 28, 2026 22:19
5a27c9f to
b5020db
Compare
Member
Author
|
bugbot review |
Member
Author
|
bugbot review |
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.
…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.
…iling source from stopping the client allFlagsState returns a state of overrides alone only when the store is not initialized and the override layer holds flags, and it reads those flags from the layer itself. Before, any layer contents were enough: a layer of segments only over a store that held LaunchDarkly data without an init marker produced a valid state of that data, and with override flags present the same data entered the state. The overlay no longer serves the layer's entries alone when the base store fails to list its items, so a client whose store fails reports an invalid state and logs the failure, as it does without a source. An exception from the source's start no longer fails client construction. The SDK logs it, closes the source, clears what the source supplied before failing, and the client runs without overrides. Closing the client stops the sink from accepting snapshots before it closes the source, so a snapshot supplied during shutdown is ignored; an exception from the source's close is still logged rather than thrown. An offline client still does not build or start the override source, and now logs a warning that it does not.
A source logs and retries a failed load, and reports a configuration error from build. If start throws anyway, the SDK logs it, closes the source, removes its snapshot, and the client runs without overrides.
When the store cannot list its flags, the state without overrides is invalid and empty. Overrides only add to it, so the state now holds the override layer's flags and stays invalid, and the failure is logged as before. A client-side SDK bootstrapped from the state uses its flags whatever $valid says, so the overridden values still reach it.
… state When the evaluation of an overridden flag throws during allFlagsState, the error result now carries the marking of the flag's own definition, so the state presents its marked reason with event tracking off, as for any override-affected flag.
kinyoklion
force-pushed
the
rlamb/overrides-java-override-layer
branch
from
October 29, 2026 17:01
22ee106 to
e95c7a9
Compare
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 e95c7a9. 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; the sink stops accepting snapshots before the source is closed, and an exception from the source'scloseis logged while the rest of the shutdown proceeds. An offline client does not build or start the override source, and logs a warning that it does not. A source that cannot be built fails client construction the same way other invalid component configuration does. An exception from the source'sstartdoes not: the SDK logs it, closes the source, clears anything the source supplied before failing, and the client runs without overrides.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 a failure of the base store propagates, as it does without the overlay.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.$validsays. The all-flags state presents an override-affected flag withtrackEventsandtrackReasonfalse 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.allFlagsState, theEXCEPTIONresult carries that flag's own override marking, so an overridden flag keeps its marked reason with event tracking off.Note
Overview
Adds experimental flag/segment overrides for the FDv2 data system: operators can configure an
OverrideSourceviaDataSystemBuilder.overrides()that pushes full snapshots into an internal layer; reads go throughOverrideOverlayStoreso per-key override definitions win over LaunchDarkly data without affecting init status or persistence.FDv2 wiring builds and starts the override source before the main data source (skipped offline); start failures log, close the source, clear the layer, and the client continues without overrides.
OverrideSinkImplapplies snapshots and emits flag-change events from merged-view diffs plus dependency fan-out.Evaluation and client API changes: overridden flags evaluate before the not-initialized short-circuit;
isFlagKnowntreats override-only flags as known when uninitialized;allFlagsStatecan return only layer flags before init (or when listing the store fails) and turns off per-flag event tracking when results are override-affected.LaunchDarkly update notifications now cross the override boundary:
DataSourceUpdatesImplexpands affected items through both the store dependency graph and the layer’sExternalDependencies, so LD segment/prerequisite changes reach dependent overridden flags.Reviewed by Cursor Bugbot for commit e95c7a9. Bugbot is set up for automated code reviews on this repo. Configure here.
Opened from termag