Repository navigation
feat: Add the override store, overlay, and data system wiring - #527
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-python-model-marker
branch
from
September 28, 2026 20:28
34322d8 to
ee313c1
Compare
kinyoklion
force-pushed
the
rlamb/overrides-python-override-store
branch
from
September 28, 2026 20:29
3fcdc80 to
19f88eb
Compare
kinyoklion
force-pushed
the
rlamb/overrides-python-model-marker
branch
from
September 30, 2026 20:31
ee313c1 to
2c70075
Compare
kinyoklion
force-pushed
the
rlamb/overrides-python-override-store
branch
from
September 30, 2026 20:31
19f88eb to
7e7b3f4
Compare
kinyoklion
force-pushed
the
rlamb/overrides-python-model-marker
branch
from
October 1, 2026 23:43
2c70075 to
5f738a6
Compare
kinyoklion
force-pushed
the
rlamb/overrides-python-override-store
branch
from
October 1, 2026 23:43
7e7b3f4 to
aa28264
Compare
kinyoklion
force-pushed
the
rlamb/overrides-python-model-marker
branch
from
October 3, 2026 01:18
5f738a6 to
0013c0e
Compare
kinyoklion
force-pushed
the
rlamb/overrides-python-override-store
branch
from
October 3, 2026 01:18
aa28264 to
1c1bd5c
Compare
Member
Author
|
bugbot review |
kinyoklion
force-pushed
the
rlamb/overrides-python-model-marker
branch
from
October 13, 2026 21:02
0013c0e to
85a71df
Compare
kinyoklion
force-pushed
the
rlamb/overrides-python-override-store
branch
from
October 13, 2026 21:02
1c1bd5c to
3cd92be
Compare
Adds the override layer described by the OVERRIDE specification: an override store that holds marked flag and segment definitions, an overlay at the store read boundary that returns the override entry for a key in preference to LaunchDarkly data and enumerates the union of both, and a sink that applies each snapshot as a single replacement and notifies flag change listeners of every flag whose evaluation may have changed, including flags that depend on a changed prerequisite or segment. The override source is an option of the FDv2 data system configuration: DataSystemConfig.override_source and ConfigBuilder.overrides(). The source is built with the data system, so invalid configuration raises from the client constructor. It starts before the data system's own threads, so its initial load completes before the constructor returns, and it is closed with the client. OverrideSource and OverrideSink are public protocols so a custom source can be supplied. The client consults the override store before its not-initialized short-circuit. An overridden flag is served before the client has LaunchDarkly data, and a flag absent from the override store still returns the client-not-ready default. The all-flags state reads through the overlay and, while the client has no LaunchDarkly data, contains only the overridden flags. The async client and data system receive the same changes, with override change notifications delivered on the event loop. The OVERRIDE specification test vectors run as a unit test through the full client stack.
…arkly data Before initialization the clients consult the override layer directly. A flag the layer does not hold gets the not-ready handling it had without an override source, for an invalid context, a store outage, and an uninitialized persistent store alike. The all-flags state is unavailable when the layer is empty. The sync client stops the components it started when the data system fails to start.
kinyoklion
force-pushed
the
rlamb/overrides-python-model-marker
branch
from
October 13, 2026 22:40
85a71df to
f3db8c6
Compare
kinyoklion
force-pushed
the
rlamb/overrides-python-override-store
branch
from
October 13, 2026 22:40
3cd92be to
cbbe302
Compare
… changes The override layer keeps a dependency graph of its own entries, rebuilt on each replacement. The store expands every changed LaunchDarkly item through both its own graph and the layer's, to a fixpoint, so a change to a prerequisite flag or a segment that an override entry references notifies that entry's flag as well. This matches how dependent flags behave for LaunchDarkly data.
Member
Author
|
bugbot review |
…iled override source from stopping the client all_flags_state returns a state of overrides alone only when no LaunchDarkly data is available and the override layer holds flags, and it reads those flags from the layer itself. Before, any layer entry was enough: a layer of segments alone 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 override source's start no longer fails client construction or start. The data system logs it, closes the source, clears what the source supplied, and the client runs without overrides. The SDK's own file-based source does not raise from start; a custom source could. Stopping the data system closes the override sink before it closes the source, so a snapshot supplied during shutdown is ignored. When the client is offline and an override source is configured, the SDK logs a warning that the source is not started.
Member
Author
|
bugbot review |
A source logs and retries a failed load, and raises a configuration error from its builder's build. If start raises anyway, the SDK logs it, closes the source, removes its snapshot, and the client runs without overrides.
Member
Author
|
bugbot review |
…nnot list its flags When the store's all-items read fails, the client logs the failure as before and, if the override layer holds flags, returns a state of those flags marked invalid instead of an empty one. Overrides only add to the state, and a client-side SDK bootstrapped from it uses its flags whatever $valid says, so the overridden values still reach it. With no override flags the state is the same invalid, empty state as without a source.
Member
Author
|
bugbot review |
…flag An evaluation that fails after reading a definition from the override layer is still marked (OVERRIDE 1.5.4). The single-flag and all-flags error paths of both clients now set overrideAffected on the EXCEPTION reason from the flag's own marker, read through is_override_definition so that the error path cannot fail on an unexpected store item.
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 aa6274b. Configure here.
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 is the third step of the flag overrides port described by the OVERRIDE specification. It is based on the model marker branch because the phases are stacked; retarget to
feat/overridesonce that branch merges.The override layer (
ldclient/impl/overrides).OverrideLayeris the override store: it holds marked shallow copies of the flag and segment definitions a source supplies, keyed by key, and is replaced wholesale on each update, so the layer's contents are exactly one snapshot at any instant and an empty snapshot clears it. Dictionaries are decoded with the model constructors, and an invalid definition raises rather than being defaulted.OverrideStoreViewis the overlay at the store read boundary: a read of a flag or segment returns the override entry when one exists and the LaunchDarkly entry otherwise, whatever the state of the base store, and an enumeration is the union of both with the override entry winning, including over a deleted item. A failure of the base enumeration propagates, as it does without the overlay. When that read fails,all_flags_statelogs the failure as before and, if the override layer holds flags, returns a state of those flags marked invalid rather than an empty one: overrides only add to the state, and a client-side SDK bootstrapped from it uses its flags whatever$validsays, so the overridden values still reach it. With no override flags the state is the same invalid, empty state as without a source. The sink stops accepting snapshots when the client is closed, and when the source fails to start it also clears what the source supplied.OverrideSinkImplapplies each snapshot as one serialized operation and notifies flag change listeners of every flag whose merged-view evaluation may have changed: entries added, removed, or changed, plus every flag that depends on them through prerequisites or segment references. An identical snapshot notifies nothing.LaunchDarkly changes reach override dependents. The layer keeps a dependency graph of its entries, rebuilt on each replacement, and the store (sync and async) expands every changed LaunchDarkly item through both its own graph and the layer's, to a fixpoint. A change to a prerequisite flag or a segment that an override entry references therefore notifies that entry's flag, the same way dependent flags are notified for LaunchDarkly data, and a chain may cross between LaunchDarkly data and overrides any number of times. Without an override source the store's behavior is unchanged.
Configuration and wiring.
OverrideSourceandOverrideSinkare public protocols inldclient.interfaces, so a custom source can be supplied.OverrideSourceBuilderandDataSystemConfig.override_source(also onAsyncDataSystemConfig) plusConfigBuilder.overrides(...)make the source an option of the FDv2 data system. The source is built with the data system, so invalid configuration raises from the client constructor like any other invalid component configuration. It starts before the data system's own threads and its initial load completes before the constructor returns; it is closed when the client is closed, after the sink stops accepting snapshots, and an exception from itscloseis logged. An exception from itsstartdoes not fail the constructor orstart: the data system logs it, closes the source, clears any snapshot the source supplied before failing, and the client runs without overrides. The SDK's own file-based source does not raise fromstart; a custom source could. The source is not built or started when the client is offline, and the SDK logs a warning that it is not used. The data system'sstorebecomes the overlay when a source is configured, so evaluation, prerequisite and segment resolution, and the all-flags read all see override precedence. The override source has no effect on initialization status, data availability, or data source status. The legacy FDv1 data system reports no override source.The client facade. Both
LDClientandAsyncLDClientconsult the override layer before the not-initialized short-circuit: an overridden flag is served before the client has LaunchDarkly data, and a flag the layer does not hold takes exactly the path it took before this feature, so a configured but empty source changes nothing with the same warning and event as before. The all-flags state reads through the overlay. While the client has no LaunchDarkly data it contains only the flags the layer holds, read from the layer rather than the overlay so that LaunchDarkly data in a store without an init marker stays out (with a once-per-client warning), and it is invalid when the layer holds no flags. Without a source this state is empty, because the SDK reports no flags before the store is initialized, so the override flags only add to what it reports; overrides never remove a flag from the state. An evaluation that fails with an unexpected exception reports theEXCEPTIONreason withoverrideAffectedwhen the flag came from the override layer, in a single evaluation and in the all-flags state (OVERRIDE 1.5.4); the marker is read throughis_override_definition, so the error path cannot fail on an unexpected store item. On the async client, override change notifications are marshalled onto the event loop, like every other change notification.Tests. Unit tests for the layer, overlay (sync and async), and sink; client tests for the gate, source lifecycle, construction errors, offline, precedence, prerequisite and segment overrides, all-flags state, and flag change and flag value change listeners, for both clients; and a runner for the OVERRIDE specification test vectors (
ldclient/testing/testdata/override-vectors/vectors.json) through the full client stack. The vector runner's per-evaluation summary marker assertion is added with the events step.SDK-3250
Note
Overview
Adds experimental flag/segment overrides that take precedence over LaunchDarkly data at evaluation time, wired through FDv2 for sync and async clients.
New stack:
OverrideLayer(snapshot store), store overlay (OverrideStoreView/ async variant), andOverrideSinkImplto apply source snapshots and fan out flag change notifications. PublicOverrideSource/OverrideSinkprotocols andConfigBuilder.overrides(...)configure an optional source onDataSystemConfig.Data system: FDv2 builds and starts the override source (skipped offline), exposes
override_layer, and serves reads through the overlay. LaunchDarkly store updates cross-notify override dependents via extended dependency tracking onStore. Failed override start logs, clears partial snapshots, and continues without overrides.Client behavior: Before the not-initialized short-circuit, evaluation reads the override layer so overridden flags work without LD connectivity;
all_flags_statecan return override-only state (with once-per-client warnings) and may stayvalid: falsewhen the base store fails but overrides exist. Evaluation errors setoverrideAffectedon reasons when the definition came from overrides. SyncLDClientrolls back components if the data system fails to start.Tests: Unit coverage for layer/overlay/sink and store external deps; broad sync/async client tests; OVERRIDE spec vectors runner.
Reviewed by Cursor Bugbot for commit aa6274b. Bugbot is set up for automated code reviews on this repo. Configure here.
Opened from termag