Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,42 @@ OpenFeature::SDK.configure do |config|
end
```

#### Blocking Provider Registration

If you need to ensure that a provider is fully initialized before continuing, you can use `set_provider_and_wait`:

```ruby
# Using the SDK directly
begin
OpenFeature::SDK.set_provider_and_wait(my_provider)
puts "Provider is ready!"
rescue OpenFeature::SDK::ProviderInitializationError => e
puts "Provider failed to initialize: #{e.message}"
puts "Original error: #{e.original_error}"
end

# With custom timeout (default is 30 seconds)
OpenFeature::SDK.set_provider_and_wait(my_provider, timeout: 60)

# Domain-specific provider
OpenFeature::SDK.set_provider_and_wait(my_provider, domain: "feature-flags")

# Via configuration block
OpenFeature::SDK.configure do |config|
begin
config.set_provider_and_wait(my_provider)
rescue OpenFeature::SDK::ProviderInitializationError => e
# Handle initialization failure
end
end
```

The `set_provider_and_wait` method:
- Waits for the provider's `init` method to complete successfully
- Raises `ProviderInitializationError` if initialization fails or times out
- Provides access to the original error and provider instance for debugging
- Uses the same thread-safe provider switching as `set_provider`

In some situations, it may be beneficial to register multiple providers in the same application.
This is possible using [domains](#domains), which is covered in more detail below.

Expand Down
22 changes: 11 additions & 11 deletions lib/open_feature/sdk/api.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# frozen_string_literal: true

require "forwardable"
require "singleton"
require 'forwardable'
require 'singleton'

require_relative "configuration"
require_relative "evaluation_context"
require_relative "evaluation_context_builder"
require_relative "evaluation_details"
require_relative "client_metadata"
require_relative "client"
require_relative "provider"
require_relative 'configuration'
require_relative 'evaluation_context'
require_relative 'evaluation_context_builder'
require_relative 'evaluation_details'
require_relative 'client_metadata'
require_relative 'client'
require_relative 'provider'

module OpenFeature
module SDK
Expand All @@ -32,7 +32,7 @@ class API
include Singleton # Satisfies Flag Evaluation API Requirement 1.1.1
extend Forwardable

def_delegators :configuration, :provider, :set_provider, :hooks, :evaluation_context
def_delegators :configuration, :provider, :set_provider, :set_provider_and_wait, :hooks, :evaluation_context

def configuration
@configuration ||= Configuration.new
Expand All @@ -48,7 +48,7 @@ def build_client(domain: nil, evaluation_context: nil)
active_provider = provider(domain:).nil? ? Provider::NoOpProvider.new : provider(domain:)

Client.new(provider: active_provider, domain:, evaluation_context:)
rescue
rescue StandardError
Client.new(provider: Provider::NoOpProvider.new, evaluation_context:)
end
end
Expand Down
50 changes: 49 additions & 1 deletion lib/open_feature/sdk/configuration.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

require_relative "api"
require 'timeout'
require_relative 'api'
require_relative 'provider_initialization_error'

module OpenFeature
module SDK
Expand Down Expand Up @@ -36,6 +38,52 @@ def set_provider(provider, domain: nil)
@providers = new_providers
end
end

# Sets a provider and waits for the initialization to complete or fail.
# This method ensures the provider is ready (or in error state) before returning.
#
# @param provider [Object] the provider to set
# @param domain [String, nil] the domain for the provider (optional)
# @param timeout [Integer] maximum time to wait for initialization in seconds (default: 30)
# @raise [ProviderInitializationError] if the provider fails to initialize or times out
def set_provider_and_wait(provider, domain: nil, timeout: 30)
@provider_mutex.synchronize do
old_provider = @providers[domain]

# Shutdown old provider (ignore errors)
begin
old_provider.shutdown if old_provider.respond_to?(:shutdown)
rescue StandardError
# Ignore shutdown errors and continue with provider initialization
end

begin
# Initialize new provider with timeout
if provider.respond_to?(:init)
Timeout.timeout(timeout) do
provider.init
end
end

# Set the new provider
new_providers = @providers.dup
new_providers[domain] = provider
@providers = new_providers
rescue Timeout::Error => e
raise ProviderInitializationError.new(
"Provider initialization timed out after #{timeout} seconds",
provider:,
original_error: e
)
rescue StandardError => e
raise ProviderInitializationError.new(
"Provider initialization failed: #{e.message}",
provider:,
original_error: e
)
end
end
end
end
end
end
26 changes: 26 additions & 0 deletions lib/open_feature/sdk/provider_initialization_error.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

module OpenFeature
module SDK
# Exception raised when a provider fails to initialize during setProviderAndWait
#
# This exception provides access to both the original error that caused the
# initialization failure and the provider instance that failed to initialize.
class ProviderInitializationError < StandardError
# @return [Object] the provider that failed to initialize
attr_reader :provider

# @return [Exception] the original error that caused the initialization failure
attr_reader :original_error

# @param message [String] the error message
# @param provider [Object] the provider that failed to initialize
# @param original_error [Exception] the original error that caused the failure
def initialize(message, provider: nil, original_error: nil)
super(message)
@provider = provider
@original_error = original_error
end
end
end
end
Loading
Loading