Repository navigation
feat: let providers own their lifecycle events - #353
Conversation
|
Warning Review limit reachedThis review ran on the open-source allowance, not this organization's plan, because the pull request author doesn't have an assigned seat. Waiting won't change this — ask an organization admin to assign them a seat, or add seats in Billing if every seat is already assigned, then retry. Next included review available in 22 minutes. View limit details
Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #353 +/- ##
=======================================
Coverage 99.66% 99.67%
=======================================
Files 32 32
Lines 906 925 +19
=======================================
+ Hits 903 922 +19
Misses 3 3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Providers can opt in to emitting their own PROVIDER_READY/PROVIDER_ERROR by defining emits_lifecycle_events? returning true (spec 2.8, Appendix E). The SDK then sends no synthetic lifecycle events for them and takes their status from their own events. - Providers with init but no marker keep the SDK-emitted events, now deprecated, and log a warning once per class. - Providers without init are READY when set_provider returns, and each new binding runs that domain's READY handlers (2.8.5.1). - InMemoryProvider drops its no-op init. - Events a provider emits from its own init record status when sent, so set_provider_and_wait from its handler returns with it. Signed-off-by: Jose Colella <jose.colella@gusto.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aa400b2 to
0d1d20d
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved status-ordering, stale-event, and concurrent-registration issues can expose incorrect readiness or clear a provider’s failure state.
3 open issues
What changed in this PR
This PR advances OpenFeature specification compliance by letting providers own lifecycle events while retaining a deprecated SDK-managed path for legacy providers. It also includes event-delivery changes from the stacked PR.
Changes:
- Adds lifecycle-event opt-in and synchronous readiness for providers without
init. - Queues nested events and runs client handlers outside handler locks.
- Updates documentation, type signatures, and tests to reflect event-driven provider status.
| File | Description |
|---|---|
spec/specification/provider_spec.rb |
Tests lifecycle ownership, readiness, and legacy behavior. |
spec/specification/events_spec.rb |
Tests event ordering, reentrancy, and handler validation. |
spec/specification/domain_scoped_provider_spec.rb |
Adjusts initialization mocking. |
spec/open_feature/sdk/provider_compatibility_spec.rb |
Updates compatibility checks after removing init. |
spec/open_feature/sdk/configuration_spec.rb |
Updates mocks and checks dispatch-state cleanup. |
sig/open_feature/sdk/provider/in_memory_provider.rbs |
Removes the init signature. |
sig/open_feature/sdk/event_dispatcher.rbs |
Declares handler validation. |
sig/open_feature/sdk/configuration.rbs |
Declares lifecycle and dispatch helpers. |
README.md |
Documents lifecycle ownership and provider migration. |
lib/open_feature/sdk/provider/in_memory_provider.rb |
Removes no-op initialization. |
lib/open_feature/sdk/event_dispatcher.rb |
Extracts reusable handler validation. |
lib/open_feature/sdk/configuration.rb |
Implements lifecycle ownership and queued event delivery. |
features/support/test_flags_provider.rb |
Makes status fixtures emit provider events. |
features/step_definitions/evaluation_steps.rb |
Replaces direct registry writes with provider setup. |
Review effort: Balanced
| end | ||
| elsif !has_init | ||
| # Spec 2.8.5.1: each new binding of a provider without init runs its domain's READY handlers | ||
| dispatch_provider_event(provider, ProviderEvent::PROVIDER_READY, token: attempt) unless old_provider.equal?(provider) |
| elsif !has_init | ||
| # Spec 2.8.5.1: each new binding of a provider without init runs its domain's READY handlers | ||
| dispatch_provider_event(provider, ProviderEvent::PROVIDER_READY, token: attempt) unless old_provider.equal?(provider) |
| token ||= running_init_attempt_on_this_thread(provider) | ||
| return if token && !accept_provider_event(provider, event_type, details, token) |


Summary
PROVIDER_READY/PROVIDER_ERRORby definingemits_lifecycle_events?returningtrue(spec 2.8, Appendix E). For those providers the SDK sends no synthetic lifecycle events, and status comes only from the provider's own events.Behavior changes worth noting
initbut no marker keeps the SDK-emitted READY/ERROR, as before. That path is now deprecated, and when a logger is configured the SDK logs a warning once per provider class. The forced READY when an already-initialized provider is set again withset_provider_and_waitnow happens only for these providers.init(for exampleNoOpProvider,InMemoryProvider, and contrib's flagd, ofrep, flipt and go-feature-flag) are READY whenset_providerreturns, with no background thread (2.8.5.1). Their READY handlers run on the thread callingset_provider, so a slow handler blocks that call. Binding one to another domain runs that domain's READY handlers.InMemoryProvider#initis removed. A subclassinitthat callssupernow raisesNoMethodError; subclasses that define their owninittake the legacy path. Contrib's meta_provider spec mocksInMemoryProvider#initwith verified doubles, so it will need a small update when it moves to this version.set_provider_and_waitstill raisesProviderInitializationErrorwheninitraises, for every kind of provider. For a marker provider that never emits READY, it returns and the status stays NOT_READY.initrecord their status when sent, soset_provider_and_waitcalled from one of its handlers returns with the new status.🤖 Generated with Claude Code