Repository navigation
feat: Split summary counters by overrideAffected and suppress individual events for marked evaluations - #365
Merged
kinyoklion merged 14 commits intoOct 29, 2026
Conversation
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
September 28, 2026 20:39
49ba8e2 to
d9e0211
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
September 28, 2026 20:39
9387b50 to
3aacd97
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 1, 2026 23:43
d9e0211 to
092ed5c
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 1, 2026 23:43
3aacd97 to
471a60c
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 3, 2026 01:34
092ed5c to
716b005
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 3, 2026 01:34
471a60c to
a750428
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 a750428. Configure here.
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 3, 2026 01:52
716b005 to
041e7a5
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 3, 2026 01:52
a750428 to
c9ce308
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 5, 2026 21:55
041e7a5 to
91ecadb
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 5, 2026 21:55
c9ce308 to
18760a7
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 5, 2026 22:12
91ecadb to
5d664c1
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 5, 2026 22:12
18760a7 to
3941b93
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 5, 2026 22:28
5d664c1 to
c875758
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 5, 2026 22:28
3941b93 to
17cf04b
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 5, 2026 23:56
c875758 to
8ad94ca
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 5, 2026 23:56
17cf04b to
fb8c7ac
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 13, 2026 21:03
8ad94ca to
b47e5ff
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 13, 2026 21:03
fb8c7ac to
e0080d0
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 13, 2026 22:13
b47e5ff to
0864b85
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
2 times, most recently
from
October 16, 2026 21:21
3469298 to
1531cd9
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 23, 2026 23:50
625311a to
0d26639
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 23, 2026 23:50
1531cd9 to
ac67f94
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 26, 2026 22:46
0d26639 to
c1ce57c
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 26, 2026 22:46
ac67f94 to
e4a339c
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 26, 2026 23:13
c1ce57c to
c93aba9
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 26, 2026 23:13
e4a339c to
e6beb28
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 27, 2026 20:23
c93aba9 to
ba14e56
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 28, 2026 21:04
0da3d0f to
917b778
Compare
…stem wiring Adds IOverrideSource and IOverrideSink, the DataSystemBuilder.Overrides option, and the override layer: an immutable store of marked entries, an overlay at the store read boundary that returns the override entry for a key in preference to LaunchDarkly data, and a sink that applies snapshots one at a time and sends flag change notifications for the flags an update may have changed, with dependency fan-out through prerequisites and segments. The FDv2 data system builds the configured source, starts it before the data sources so its initial load completes inside the client constructor, and disposes it with the client. The client consults the override store before its not-initialized short-circuit, so an overridden flag is served before LaunchDarkly data arrives. AllFlagsState reads through the overlay and presents override-affected flags with event tracking off. The OVERRIDE specification's test vectors run as a unit test.
A definition whose dependencies cannot be computed, such as a prerequisite without a key or a segment match on a value that is not a string, is logged and contributes no dependency edges, so every directly changed key is still notified. A failure to determine or deliver the notifications is logged instead of reaching the override source.
…uit and dispose on a failed start Before the client is initialized, an evaluation proceeds only when the override layer holds the flag; otherwise it takes the same path as without an override source. The exception paths of evaluation and of the all-flags state keep the override indicator when the definition came from the layer. An override source whose Build or Start throws fails the client constructor after the components created so far are disposed.
…n changes 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. An entry whose references cannot be read contributes no edges, the same as the change computation skips it.
An exception from the override source's Start no longer fails the client constructor. The data system logs it, disposes the source, and the client runs as it does without a source: an application in an incident is better served by a client that runs without its overrides than by one that does not start. The SDK's own file-based source never throws from Start, since it logs a failed load and retries; a custom source could. The interface documents the behavior.
A source can supply a snapshot through the sink and then throw from Start. The data system now closes the sink after disposing the failed source: the sink clears the layer, notifying the flags that were briefly overridden, and ignores a late snapshot from the disposed source. The client then runs without overrides, as the log says, and its all-flags state agrees.
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-override-layer
branch
from
October 28, 2026 21:23
4c1e37c to
87c85e3
Compare
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 28, 2026 21:23
917b778 to
96a4008
Compare
…source down safely AllFlagsState returns a state of overrides alone only when the client is not initialized, the store is not initialized, and the override layer holds flags, and it reads those flags from the layer itself. Before, a configured source was enough: an empty layer over a store that held LaunchDarkly data without an init marker produced a valid state of that data, and with overrides 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 an initialized client whose store fails reports an invalid state and logs the failure, as it does without a source. Disposing the client closes the override sink before it disposes the source, so a snapshot supplied during shutdown is ignored, and an exception from the source's Dispose is logged instead of stopping the rest of the shutdown. MigrationVariation keeps the override-affected marking when it replaces the reason of an unrecognized stage with WRONG_TYPE.
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 28, 2026 21:53
96a4008 to
521a20b
Compare
An offline client does not build or start the override source, so its overrides are not served and a configuration error in it is not reported. The SDK now logs a warning when that happens, and the Overrides configuration method documents it.
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 28, 2026 22:02
521a20b to
f7eafb9
Compare
A source logs and retries a failed load, and reports a configuration error from Build. If Start throws anyway, the SDK logs it, disposes the source, removes its snapshot, and the client runs without overrides.
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 28, 2026 22:35
f7eafb9 to
52b82ae
Compare
… store fails 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 still logged. A client-side SDK bootstrapped from the state uses its flags whatever its validity says, so the overridden values reach it. With no override flags the state is invalid and empty, as without a source.
…ual events for marked evaluations EventTypes.EvaluationEvent gains an OverrideAffected field. A marked evaluation produces no individual feature event and no debug event, whatever the flag requests, and is counted under a separate summary counter whose key includes the marker. The summary output writes "overrideAffected": true on such a counter and omits the property otherwise, as defined by the OVERRIDE specification.
kinyoklion
force-pushed
the
rlamb/overrides-dotnet-events-internal
branch
from
October 29, 2026 16:45
52b82ae to
56e48ec
Compare
Base automatically changed from
rlamb/overrides-dotnet-override-layer
to
dotnet-server-sdk-v9
October 29, 2026 20:11
…e and debug paths The prerequisite and debug tests checked only that a summary was sent. They now check that its counter carries overrideAffected, so a marker lost on either path fails them. The aggregated and per-context summarizer tests gain a marked case, so a wrapper that dropped the new argument fails them too.
This was referenced Oct 29, 2026
kinyoklion
pushed a commit
that referenced
this pull request
Oct 29, 2026
…alpha (#385) 🤖 I have created a release *beep* *boop* --- ## [3.12.0-alpha](LaunchDarkly.InternalSdk-v3.11.1...LaunchDarkly.InternalSdk-v3.12.0-alpha) (2026-10-29) ### Features * Split summary counters by overrideAffected and suppress individual events for marked evaluations ([#365](#365)) ([c9b327b](c9b327b)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > Release Please PR that cuts **LaunchDarkly.InternalSdk `3.12.0-alpha`**, updating the release manifest, package `Version`, provenance docs, and changelog. > > The release notes the **3.12.0** feature from [#365](#365): analytics **summary counters are keyed by `overrideAffected`**, and evaluations with that marker **skip individual feature/debug events** while still contributing to summaries. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 5f83938. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
kinyoklion
added a commit
that referenced
this pull request
Oct 29, 2026
<!-- TERMAG_PR_BODY_BEGIN --> ## Summary Makes the server SDK hand the override-affected marking to the event processor, as defined by the OVERRIDE specification. `EventProcessorTypes.EvaluationEvent` gains an `OverrideAffected` property, `EventFactory` sets it from the evaluation result for every evaluation event and every prerequisite record, and `DefaultEventProcessorWrapper` passes it to the InternalSdk event processor, which then suppresses individual feature and debug events for the marked evaluation and counts it under a separate summary counter that carries the marker. The marking is carried whether or not the caller asked for reasons. A wrong-type result keeps the marking: the replacement error reason and the event for that evaluation both report it, because the evaluation read the same definitions. The specification's test vectors now also assert the per-evaluation summary contribution, and new client tests cover the marked evaluation record, the prerequisite tree in which only one leaf is overridden, and the event output of a full flush, in which only the unaffected sibling prerequisite produces a feature event while all four flags have tracking on. It depends on the InternalSdk events change, #365, released as `LaunchDarkly.InternalSdk` 3.12.0-alpha, which the server now references. [SDK-3248](https://launchdarkly.atlassian.net/browse/SDK-3248) [SDK-3248]: https://launchdarkly.atlassian.net/browse/SDK-3248?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > The server SDK now passes **override-affected** marking from evaluations into analytics: `EvaluationEvent` gains **`OverrideAffected`**, **`EventFactory`** sets it from the evaluation reason (including prerequisite and wrong-type default-value events), and **`DefaultEventProcessorWrapper`** forwards it to InternalSdk so marked evaluations skip individual feature/debug events and appear in summary counters with the marker—even when the caller did not request reasons. > > The package reference bumps to **`LaunchDarkly.InternalSdk` 3.12.0-alpha** (depends on the InternalSdk events change). **Override vector tests** assert `summaryOverrideAffected`, and new **`LdClientOverrideEventsTest`** coverage exercises marking, prerequisite trees, wrong-type paths, flush output, and the default event processor wrapper. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 2b8016e. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- TERMAG_PR_BODY_END --> <!-- termag: box=i-0575d1c54853058d7 project=projects --> <sub>Opened from termag</sub>
kinyoklion
pushed a commit
that referenced
this pull request
Oct 29, 2026
…ha.1 (#382) 🤖 I have created a release *beep* *boop* --- ## [9.0.0-alpha.1](LaunchDarkly.ServerSdk-v9.0.0-alpha.0...LaunchDarkly.ServerSdk-v9.0.0-alpha.1) (2026-10-29) ### Features * Add a shared file data reloader, poller, and watcher ([#361](#361)) ([4eed241](4eed241)) * Add the file-based override source ([#367](#367)) ([b834131](b834131)) * Add the override marker to the flag and segment models and mark evaluations ([#363](#363)) ([21f666a](21f666a)) * Add the override store, overlay, source interfaces, and data system wiring ([#364](#364)) ([e1748a1](e1748a1)) * Mark evaluation events as override-affected ([#366](#366)) ([7c873a0](7c873a0)) * Split summary counters by overrideAffected and suppress individual events for marked evaluations ([#365](#365)) ([c9b327b](c9b327b)) * Support the flag-overrides capability in the contract test service ([#368](#368)) ([b80d6aa](b80d6aa)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Overview** > This is a **Release Please** cut that publishes **LaunchDarkly.ServerSdk `9.0.0-alpha.1`**. The diff only updates release metadata: `.release-please-manifest.json`, `LaunchDarkly.ServerSdk.csproj` `<Version>`, `PROVENANCE.md` verification example, and a new **`9.0.0-alpha.1`** section in `CHANGELOG.md`. > > The changelog entry documents what ships in this alpha (already merged elsewhere): **local flag overrides** via override store/overlay/source wiring and a **file-based override source** with shared file reloader/poller/watcher; **override markers** on flag/segment models and evaluations; **analytics** changes that mark override-affected evaluations, split summary counters by `overrideAffected`, and suppress individual events when appropriate; plus **contract-test** support for the flag-overrides capability. > > Reviewers can treat this as a **versioning and release-notes sanity check**—no application logic changes appear in the diff itself. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 81f0588. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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
Extends the analytics event processing in LaunchDarkly.InternalSdk for override-affected evaluations, as defined by the OVERRIDE specification.
EventTypes.EvaluationEventgains anOverrideAffectedfield. When it is set, the event processor produces no individual feature event and no debug event for the evaluation, whatever the flag'sTrackEventsandDebugEventsUntilDaterequest. The evaluation is still counted in summary events, and the marker becomes part of the summary counter key, so override-affected and other evaluations of the same flag key, variation, and version accumulate into separate counters instead of being collapsed together. The summary event output writes"overrideAffected": trueon a marked counter and omits the property otherwise, following the existingunknownmarker. The summarizer interface carries the marker through both the aggregated and the per-context summarizers.Nothing sets the field yet; the server SDK does so in the next change. Every existing event path is unchanged when the field is false, which the existing event processor and output tests confirm. New tests cover the suppression of feature and debug events, the suppression of a marked prerequisite record, the separate counters, and the counter serialization.
This PR is based on the branch of the override layer change so that the whole flag overrides series applies as one linear sequence; it uses nothing from that branch.
SDK-3248
Note
Overview
Adds an experimental flag/segment override layer to the FDv2 data system so operators can force known flag state per key without affecting initialization or data-source status.
Configuration and plumbing:
DataSystemBuilder.Overrides()wires an optionalIOverrideSourceintoDataSystemConfiguration. FDv2 builds anOverrideLayer,OverrideSink, andOverrideOverlayon the store read path; FDv1 exposes empty/no-op override APIs. Offline mode skips starting the source (warn only). Start/dispose failures are logged and the client continues without overrides when appropriate.Evaluation and client behavior: Overridden entries win on read; flags can be evaluated (and
AllFlagsStatecan return override-only payloads) before LaunchDarkly data is available when the layer holds them. Evaluation reasons carryoverrideAffectedthrough errors and migration paths;AllFlagsStatedisables per-flag tracking fields for override-affected results.Change notifications: LaunchDarkly updates fan out through combined dependency tracking (
IExternalDependencieson the override layer), and override snapshots notify listeners via the same flag-change path with merged-view diffing inOverrideChanges.Analytics:
EvaluationEvent.OverrideAffectedsuppresses individual feature and debug events while still counting summaries; counters are keyed separately and serialize"overrideAffected": truewhen set.Extensive unit/integration tests cover the layer, overlay, sink, builder, client scenarios, OVERRIDE spec vectors, and event summarizer/processor output.
Reviewed by Cursor Bugbot for commit e29784c. Bugbot is set up for automated code reviews on this repo. Configure here.