Repository navigation
fix: Notify overridden flags when the LaunchDarkly data they depend on changes - #2108
kinyoklion wants to merge 1 commit into
Conversation
…n changes A flag override can reference LaunchDarkly data: a rule that matches a LaunchDarkly segment, or a prerequisite that is a LaunchDarkly flag. The data system's dependency tracking is built from the LaunchDarkly data alone, so when such a segment or flag changed, the flags whose overrides depend on it were not notified through the flag change listeners. The override layer now keeps the dependency edges of its own entries, rebuilt on every replacement, and the FDv2 data source updates ask it for the override entries that depend on a changed item. The chain can cross in both directions, a LaunchDarkly flag with an overridden prerequisite that references a LaunchDarkly segment for example, so each newly reached item is expanded through both sets of edges until nothing new is found. The namespaced data set behind the dependency tracker now uses maps without a prototype, so an item keyed like a property of Object (constructor, __proto__) is an ordinary entry instead of a lookup that returns an inherited function and throws.
|
bugbot review |
|
@launchdarkly/js-sdk-common size report |
|
@launchdarkly/js-client-sdk-common size report |
|
@launchdarkly/js-client-sdk size report |
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 b3e6415. Configure here.
Summary
A flag override can reference LaunchDarkly data: a rule that matches a LaunchDarkly segment, or a prerequisite that is a LaunchDarkly flag. The FDv2 data source updates compute flag change notifications from a dependency tracker built from LaunchDarkly data alone, so a change to such a segment or flag did not notify the flags whose overrides depend on it. The override sink already fans an override change out over the merged view; this is the other direction.
The override layer now keeps the dependency edges of its own entries, rebuilt with the contents on every replacement, and exposes them through the same
updateModifiedItemsshape the tracker has.TransactionalDataSourceUpdatestakes that as an optional external dependency source and, for each modified LaunchDarkly item, expands the affected set through both the LaunchDarkly edges and the override edges until nothing new is reached. A chain can cross in either direction, for example a LaunchDarkly flag whose overridden prerequisite references a LaunchDarkly segment, so one pass through each tracker is not enough. Each tracker gets its own working set because a tracker skips an item that is already in the set it is given.NamespacedDataSet, which backsDependencyTracker, now uses maps without a prototype. Feeding it the override layer's entries exposed the existing problem that a key named like a property ofObject(a flag keyedconstructor,__proto__) returned an inherited function fromgetand made the tracker throw. This is the same hazard the override layer already guards against in its own maps.Behavior for flags with no overrides is unchanged: with no external dependencies the expansion is the single tracker walk it was before. The FDv1
DataSourceUpdatespath is unchanged because overrides exist only with the FDv2 data system.Spec: OVERRIDE 1.4.2.1 requires the notifications for override changes and reasons that reusing the notification path handles dependent flags the same way as for LaunchDarkly data; this closes the gap where a LaunchDarkly change did not reach override dependents. Raised by Bugbot on the Java (#222) and .NET (#364) stacks; the same change is going into Go and the four other stacks.
Tests
__tests__/overrides/OverrideLayer.test.ts: the layer reports the entries that depend on a segment or a prerequisite, transitively through other entries, and forgets edges of entries a replacement removed.__tests__/data_sources/DataSourceUpdates.test.ts: with an override layer as the external dependencies, an upsert of a LaunchDarkly segment notifies the overridden flag that references it; a chain that crosses from a LaunchDarkly flag into an override entry and back to a LaunchDarkly segment notifies both flags; an unrelated item does not notify the override.__tests__/LDClientImpl.overrideDependents.test.ts: an FDv2 polling client with an override whose rule matches a LaunchDarkly segment; after the initial notifications are drained, a poll that changes only the segment notifies only the overridden flag.__tests__/data_sources/NamespacedDataSet.test.ts: entries keyed like properties ofObjectare stored, found, enumerated, removed, and tracked.Whole
@launchdarkly/js-server-sdk-commonsuite 1565 total, 7 skipped, all passed;@launchdarkly/node-server-sdk84 passed; roottsc, oxlint, oxfmt clean.Note
Overview
Fixes a gap where overridden flags that reference LaunchDarkly segments or prerequisites did not receive change notifications when only the LaunchDarkly side updated.
OverrideLayer now rebuilds a dependency graph for override entries on each snapshot and exposes
updateModifiedItemslike the main tracker. TransactionalDataSourceUpdates accepts an optional external dependency source (the shared override layer) and, when computing affected items, walks both LaunchDarkly and override edges in a loop so chains can cross layers (e.g. LD flag → overridden prerequisite → LD segment).LDClientImpl creates one
OverrideLayerearlier and passes it into transactional updates as well as the read overlay. NamespacedDataSet uses prototype-less maps so keys such asconstructoror__proto__do not break dependency tracking.New tests cover layer dependency reporting, transactional updates with overrides, end-to-end FDv2 polling, and
NamespacedDataSetedge cases.Reviewed by Cursor Bugbot for commit b3e6415. Bugbot is set up for automated code reviews on this repo. Configure here.