-
Notifications
You must be signed in to change notification settings - Fork 54
chore: Create FDv1 datasystem implementation #339
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
jsonbailey
merged 10 commits into
main
from
jb/sdk-1541/enable-fdv1-to-use-new-datasystem
Dec 4, 2025
Merged
Changes from 3 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
66b8876
chore: Create FDv1 datasystem implementation
jsonbailey 2763e07
set proper availability for daemon mode
jsonbailey 473de38
allow flag tracker to be created by calling code
jsonbailey 8784a7e
don't create new update processor is already started
jsonbailey 2eca9c9
always return cached for ldd mode
jsonbailey e89b7fb
fix lint
jsonbailey 8e84bd1
fix missing require in null update processor
jsonbailey 421e857
clean up and link docs
jsonbailey 67a6c0f
use ruby file and folder naming conventions
jsonbailey 45e8c70
return refreshed for backwards compatibility if ldd mode
jsonbailey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| require 'concurrent' | ||
|
|
||
| module LaunchDarkly | ||
| module Impl | ||
| module DataSource | ||
| # | ||
| # A minimal UpdateProcessor implementation used when the SDK is in offline mode | ||
| # or daemon (LDD) mode. It does nothing except mark itself as initialized. | ||
| # | ||
| # @private | ||
| # | ||
| class NullUpdateProcessor | ||
| include LaunchDarkly::Interfaces::DataSource | ||
|
|
||
| # | ||
| # Creates a new NullUpdateProcessor. | ||
| # | ||
| def initialize | ||
| @ready = Concurrent::Event.new | ||
| end | ||
|
|
||
| # | ||
| # Starts the data source. Since this is a null implementation, it immediately | ||
| # sets the ready event to indicate initialization is complete. | ||
| # | ||
| # @return [Concurrent::Event] The ready event | ||
| # | ||
| def start | ||
| @ready.set | ||
| @ready | ||
| end | ||
|
|
||
| # | ||
| # Stops the data source. This is a no-op for the null implementation. | ||
| # | ||
| # @return [void] | ||
| # | ||
| def stop | ||
| # Nothing to do | ||
| end | ||
|
|
||
| # | ||
| # Checks if the data source has been initialized. | ||
| # | ||
| # @return [Boolean] Always returns true since this is a null implementation | ||
| # | ||
| def initialized? | ||
| true | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,205 @@ | ||
| require 'concurrent' | ||
| require 'ldclient-rb/impl/datasystem' | ||
| require 'ldclient-rb/impl/data_source' | ||
| require 'ldclient-rb/impl/data_store' | ||
| require 'ldclient-rb/impl/datasource/null_processor' | ||
| require 'ldclient-rb/impl/broadcaster' | ||
|
|
||
| module LaunchDarkly | ||
| module Impl | ||
| module DataSystem | ||
| # | ||
| # FDv1 wires the existing v1 data source and store behavior behind the | ||
| # generic DataSystem surface. | ||
| # | ||
| # @private | ||
| # | ||
| class FDv1 | ||
| include LaunchDarkly::Impl::DataSystem | ||
|
|
||
| # | ||
| # Creates a new FDv1 data system. | ||
| # | ||
| # @param sdk_key [String] The SDK key | ||
| # @param config [LaunchDarkly::Config] The SDK configuration | ||
| # | ||
| def initialize(sdk_key, config) | ||
| @sdk_key = sdk_key | ||
| @config = config | ||
| @shared_executor = Concurrent::SingleThreadExecutor.new | ||
|
|
||
| # Set up data store plumbing | ||
| @data_store_broadcaster = LaunchDarkly::Impl::Broadcaster.new(@shared_executor, @config.logger) | ||
| @data_store_update_sink = LaunchDarkly::Impl::DataStore::UpdateSink.new( | ||
| @data_store_broadcaster | ||
| ) | ||
|
|
||
| # Wrap the data store with client wrapper (must be created before status provider) | ||
| @store_wrapper = LaunchDarkly::Impl::FeatureStoreClientWrapper.new( | ||
| @config.feature_store, | ||
| @data_store_update_sink, | ||
| @config.logger | ||
| ) | ||
|
|
||
| # Create status provider with store wrapper | ||
| @data_store_status_provider = LaunchDarkly::Impl::DataStore::StatusProvider.new( | ||
| @store_wrapper, | ||
| @data_store_update_sink | ||
| ) | ||
|
|
||
| # Set up data source plumbing | ||
| @data_source_broadcaster = LaunchDarkly::Impl::Broadcaster.new(@shared_executor, @config.logger) | ||
| @flag_change_broadcaster = LaunchDarkly::Impl::Broadcaster.new(@shared_executor, @config.logger) | ||
| @data_source_update_sink = LaunchDarkly::Impl::DataSource::UpdateSink.new( | ||
| @store_wrapper, | ||
| @data_source_broadcaster, | ||
| @flag_change_broadcaster | ||
| ) | ||
| @data_source_status_provider = LaunchDarkly::Impl::DataSource::StatusProvider.new( | ||
| @data_source_broadcaster, | ||
| @data_source_update_sink | ||
| ) | ||
|
|
||
| # Ensure v1 processors can find the sink via config for status updates | ||
| @config.data_source_update_sink = @data_source_update_sink | ||
|
|
||
| # Update processor created in start(), because it needs the ready event | ||
| @update_processor = nil | ||
|
|
||
| # Diagnostic accumulator provided by client for streaming metrics | ||
| @diagnostic_accumulator = nil | ||
| end | ||
|
|
||
| # | ||
| # Starts the v1 update processor and returns immediately. The returned event | ||
| # will be set by the processor upon first successful initialization or upon permanent failure. | ||
| # | ||
| # @return [Concurrent::Event] Event that will be set when initialization is complete | ||
| # | ||
| def start | ||
| @update_processor = make_update_processor | ||
| @update_processor.start | ||
| end | ||
|
|
||
| # | ||
| # Halts the data system, stopping the update processor and shutting down the executor. | ||
| # | ||
| # @return [void] | ||
| # | ||
| def stop | ||
| @update_processor&.stop | ||
| @shared_executor.shutdown | ||
| end | ||
|
|
||
| # | ||
| # Returns the feature store wrapper used by this data system. | ||
| # | ||
| # @return [LaunchDarkly::Impl::DataStore::ClientWrapper] | ||
| # | ||
| def store | ||
| @store_wrapper | ||
| end | ||
|
|
||
| # | ||
| # Sets the diagnostic accumulator for streaming initialization metrics. | ||
| # This should be called before start() to ensure metrics are collected. | ||
| # | ||
| # @param diagnostic_accumulator [DiagnosticAccumulator] The diagnostic accumulator | ||
| # @return [void] | ||
| # | ||
| def set_diagnostic_accumulator(diagnostic_accumulator) | ||
| @diagnostic_accumulator = diagnostic_accumulator | ||
| end | ||
|
|
||
| # | ||
| # Returns the data source status provider. | ||
| # | ||
| # @return [LaunchDarkly::Interfaces::DataSource::StatusProvider] | ||
| # | ||
| def data_source_status_provider | ||
| @data_source_status_provider | ||
| end | ||
|
|
||
| # | ||
| # Returns the data store status provider. | ||
| # | ||
| # @return [LaunchDarkly::Interfaces::DataStore::StatusProvider] | ||
| # | ||
| def data_store_status_provider | ||
| @data_store_status_provider | ||
| end | ||
|
|
||
| # | ||
| # Returns the broadcaster for flag change notifications. | ||
| # | ||
| # @return [LaunchDarkly::Impl::Broadcaster] | ||
| # | ||
| def flag_change_broadcaster | ||
| @flag_change_broadcaster | ||
| end | ||
|
|
||
| # | ||
| # Indicates what form of data is currently available. | ||
| # | ||
| # This is calculated dynamically based on current system state. | ||
| # | ||
| # @return [Symbol] One of DataAvailability constants | ||
| # | ||
| def data_availability | ||
| return DataAvailability::DEFAULTS if @config.offline? | ||
|
|
||
| unless @config.use_ldd? | ||
| return DataAvailability::REFRESHED if @update_processor && @update_processor.initialized? | ||
| end | ||
|
jsonbailey marked this conversation as resolved.
Outdated
|
||
|
|
||
| return DataAvailability::CACHED if @store_wrapper.initialized? | ||
|
|
||
| DataAvailability::DEFAULTS | ||
| end | ||
|
|
||
| # | ||
| # Indicates the ideal form of data attainable given the current configuration. | ||
| # | ||
| # @return [Symbol] One of DataAvailability constants | ||
| # | ||
| def target_availability | ||
| return DataAvailability::DEFAULTS if @config.offline? | ||
| return DataAvailability::CACHED if @config.use_ldd? | ||
|
|
||
| DataAvailability::REFRESHED | ||
| end | ||
|
|
||
| # | ||
| # Creates the appropriate update processor based on the configuration. | ||
| # | ||
| # @return [Object] The update processor | ||
| # | ||
| private def make_update_processor | ||
| # Handle custom data source (factory or instance) | ||
| if @config.data_source | ||
| return @config.data_source unless @config.data_source.respond_to?(:call) | ||
|
|
||
| # Factory - call with appropriate arity | ||
| return @config.data_source.arity == 3 ? | ||
| @config.data_source.call(@sdk_key, @config, @diagnostic_accumulator) : | ||
| @config.data_source.call(@sdk_key, @config) | ||
| end | ||
|
|
||
| # Create default data source based on config | ||
| return LaunchDarkly::Impl::DataSource::NullUpdateProcessor.new if @config.offline? || @config.use_ldd? | ||
|
|
||
| if @config.stream? | ||
| require 'ldclient-rb/stream' | ||
| return LaunchDarkly::StreamProcessor.new(@sdk_key, @config, @diagnostic_accumulator) | ||
| end | ||
|
|
||
| # Polling processor | ||
| require 'ldclient-rb/polling' | ||
| requestor = LaunchDarkly::Requestor.new(@sdk_key, @config) | ||
| LaunchDarkly::PollingProcessor.new(@config, requestor) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
|
|
||
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
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.