feat: Add the FDv2 data source orchestrator to the client SDK - #612
feat: Add the FDv2 data source orchestrator to the client SDK#612beekld wants to merge 4 commits into
Conversation
e03fed7 to
d3da070
Compare
d3da070 to
22c46d4
Compare
22c46d4 to
ae8dc46
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ae8dc46. Configure here.
| } | ||
| sink_->Apply(context_, std::move(change_set.change_set), from_cache); | ||
| PublishState(DataSourceStatus::DataSourceState::kValid); | ||
| } |
There was a problem hiding this comment.
Shutdown does not fence updates
High Severity
Close() does not stop in-flight work from writing flags or status. ApplyResult never consults closed_, OnInitializerResult applies a changeset before checking it, and PublishState drops the lock before SetState. ShutdownAsync then posts its completion immediately, so an identify restart can apply the old context over the new cache or clobber the replacement source's status.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit ae8dc46. Configure here.
| } | ||
| sink_->Apply(context_, std::move(change_set.change_set), from_cache); | ||
| PublishState(DataSourceStatus::DataSourceState::kValid); | ||
| } |
There was a problem hiding this comment.
Empty changeset marks source valid
Medium Severity
ApplyResult publishes kValid for every changeset, including a kNone result that carries no selector and no flags. That leaves initializing before a basis exists. The status manager then treats later errors as real interruptions, and a StartAsync waiter can resolve as successful before ReportExhausted moves the source to kShutdown.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit ae8dc46. Configure here.


Summary
Adds the client SDK's FDv2 data source -- the orchestrator that runs initializers to load a basis, then rotates synchronizers to keep it current.
Implemented here:
Also moves the shared orchestration primitives out of the server SDK into
libs/internalso both SDKs can share them. That server change is a behavior-neutral relocation.Notes:
Start()blocks on cache initialization, so cached flags are immediately evaluable. This matches the behavior of the FDv1 client. It also matches Android. However, Flutter loads the cache asynchronously instead.Note
Overview
Introduces
FDv2DataSourcefor the client SDK: anIDataSourcethat chains initializers (until a selector-bearing basis exists), then synchronizers with fallback/recovery conditions, applies changesets to the flag store (withfrom_cachetagging), publishes data-source status, and exposesEnvironmentId().Start()synchronously runs cache initializers so cached flags are evaluable before the executor runs, including cache-only / offline success paths.Refactors shared FDv2 orchestration into
libs/internal(conditions,SourceManager, condition interfaces) underlaunchdarkly::internal::data_sources, so client and server both consume the same code. Conditions now take aSourceSignal(kChangeSet/kInterrupted/kOther) instead of full source results; orchestrators map results viaClassifyResult.SourceManagerbecomes a header-only template; the server SDK drops its local copies andsource_manager.cpp.Adds broad unit tests for the client orchestrator and updates internal/server tests for the new APIs.
Reviewed by Cursor Bugbot for commit ae8dc46. Bugbot is set up for automated code reviews on this repo. Configure here.