-
Notifications
You must be signed in to change notification settings - Fork 55
fix: provider state ownership via events #385
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| --- | ||
| id: appendix-e | ||
| title: "Appendix E: Migrations" | ||
| description: Migration guidance for breaking spec changes | ||
| sidebar_position: 6 | ||
| --- | ||
|
|
||
| # Appendix E: Migrations | ||
|
|
||
| This appendix provides non-normative guidance for provider authors and SDK authors on migrating to new or changed specification requirements. | ||
|
|
||
| ## Provider Lifecycle Event Emission | ||
|
|
||
| ### Background | ||
|
|
||
| Prior to `v0.9.0`, the SDK emitted synthetic lifecycle events (`PROVIDER_READY`, `PROVIDER_ERROR`) on behalf of providers after lifecycle methods (`initialize`, `shutdown`, `on context change`) returned. | ||
| This created a race condition in multi-threaded SDKs: the provider could emit events from background threads concurrently with SDK-emitted synthetic events, resulting in incorrect event ordering and inconsistent status. | ||
|
|
||
| The spec now requires providers to emit their own lifecycle events. | ||
| The SDK derives provider status entirely from the provider's event stream (see [provider status](./sections/02-providers.md#28-provider-status), [requirement 5.3.5](./sections/05-events.md#requirement-535)). | ||
|
|
||
| ### For provider authors | ||
|
|
||
| Providers must now emit events for all state transitions, including those resulting from lifecycle methods: | ||
|
|
||
| - `PROVIDER_READY` after successful initialization | ||
| - `PROVIDER_ERROR` after failed initialization (with appropriate error code) | ||
| - `PROVIDER_RECONCILING` when beginning context reconciliation (static-context paradigm) | ||
| - `PROVIDER_CONTEXT_CHANGED` after successful context reconciliation (static-context paradigm) | ||
| - `PROVIDER_ERROR` after failed context reconciliation (static-context paradigm) | ||
|
|
||
| To signal to the SDK that your provider emits its own lifecycle events, implement the opt-in marker defined by the SDK (e.g. an interface, boolean property, or type-level tag). | ||
|
|
||
| Providers that do not implement this marker will continue to work via the SDK's legacy compatibility path (see below). | ||
|
|
||
| ### For SDK authors | ||
|
|
||
| SDKs must detect whether a provider emits its own lifecycle events, via an opt-in marker (e.g. an interface, boolean property, or type-level tag). | ||
|
|
||
| - **Marker present:** the SDK does not emit synthetic lifecycle events. It derives status solely from the provider's event stream. | ||
| - **Marker absent (legacy):** the SDK emits synthetic lifecycle events after lifecycle methods return, as before. This path is deprecated. | ||
|
|
||
| The legacy path should be deprecated in the release that introduces the marker, with removal targeted for the next major version. | ||
| SDK authors should update any first-party providers and provider base classes to emit their own lifecycle events. | ||
|
Comment on lines
+43
to
+44
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes much sense to me! |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -463,9 +463,9 @@ stateDiagram-v2 | |
|
|
||
| > The `client` **MUST** define a `provider status` accessor which indicates the readiness of the associated provider, with possible values `NOT_READY`, `READY`, `STALE`, `ERROR`, or `FATAL`. | ||
|
|
||
| The SDK at all times maintains an up-to-date state corresponding to the success/failure of the last lifecycle method (`initialize`, `shutdown`, `on context change`) or emitted event. | ||
| The SDK derives provider status from events emitted by the provider and keeps it up-to-date as events are received. | ||
|
|
||
| see [provider status](../types.md#provider-status) | ||
| see [provider status](../types.md#provider-status), [provider events](./05-events.md#51-provider-events) | ||
|
|
||
| #### Condition 1.7.2 | ||
|
|
||
|
|
@@ -477,55 +477,37 @@ see: [static-context paradigm](../glossary.md#static-context-paradigm) | |
|
|
||
| ##### Conditional Requirement 1.7.2.1 | ||
|
|
||
| > In addition to `NOT_READY`, `READY`, `STALE`, or `ERROR`, the `provider status` accessor must support possible value `RECONCILING`. | ||
| > In addition to `NOT_READY`, `READY`, `STALE`, `ERROR`, or `FATAL`, the `provider status` accessor **MUST** support possible value `RECONCILING`. | ||
|
|
||
| In the static context paradigm, the implementation must define a `provider status` indicating that a provider is reconciling its internal state due to a context change. | ||
|
|
||
| #### Requirement 1.7.3 | ||
|
|
||
| > The client's `provider status` accessor **MUST** indicate `READY` if the `initialize` function of the associated provider terminates normally. | ||
| > The client's `provider status` accessor **MUST** indicate `READY` after the provider emits `PROVIDER_READY` following initialization. | ||
|
|
||
| Once the provider has initialized, the `provider status` should indicate the provider is ready to be used to evaluate flags. | ||
| see: [provider status requirements](./02-providers.md#28-provider-status), [event-status mapping](./05-events.md#requirement-535) | ||
|
|
||
| #### Requirement 1.7.4 | ||
|
|
||
| > The client's `provider status` accessor **MUST** indicate `ERROR` if the `initialize` function of the associated provider terminates abnormally. | ||
| > The client's `provider status` accessor **MUST** indicate `ERROR` after the provider emits `PROVIDER_ERROR` following initialization. | ||
|
|
||
| If the provider has failed to initialize, the `provider status` should indicate the provider is in an error state. | ||
| see: [provider status requirements](./02-providers.md#28-provider-status), [event-status mapping](./05-events.md#requirement-535) | ||
|
|
||
| #### Requirement 1.7.5 | ||
|
|
||
| > The client's `provider status` accessor **MUST** indicate `FATAL` if the `initialize` function of the associated provider terminates abnormally and indicates `error code` `PROVIDER_FATAL`. | ||
| > The client's `provider status` accessor **MUST** indicate `FATAL` after the provider emits `PROVIDER_ERROR` with error code `PROVIDER_FATAL` following initialization. | ||
|
|
||
| If the provider has failed to initialize, the `provider status` should indicate the provider is in an error state. | ||
| see: [provider status requirements](./02-providers.md#28-provider-status), [event-status mapping](./05-events.md#requirement-535) | ||
|
|
||
| #### Requirement 1.7.6 | ||
|
|
||
| > The client **MUST** default, run error hooks, and indicate an error if flag resolution is attempted while the provider is in `NOT_READY`. | ||
|
|
||
| The client defaults and returns the `PROVIDER_NOT_READY` `error code` if evaluation is attempted before the provider is initialized (the provider is still in a `NOT_READY` state). | ||
| The SDK avoids calling the provider's resolver functions entirely ("short-circuits") if the provider is in this state. | ||
|
|
||
| see: [error codes](../types.md#error-code), [flag value resolution](./02-providers.md#22-flag-value-resolution) | ||
|
|
||
| #### Requirement 1.7.7 | ||
|
|
||
| > The client **MUST** default, run error hooks, and indicate an error if flag resolution is attempted while the provider is in `FATAL`. | ||
|
|
||
| The client defaults and returns the `PROVIDER_FATAL` `error code` if evaluation is attempted after the provider has transitioned to an irrecoverable error state. | ||
| The SDK avoids calling the provider's resolver functions entirely ("short-circuits") if the provider is in this state. | ||
|
|
||
| see: [error codes](../types.md#error-code), [flag value resolution](./02-providers.md#22-flag-value-resolution) | ||
|
Comment on lines
-504
to
-518
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we have the client defaults case on evaluation during NOT_READY/FATAL defined somewhere else? We could consider moving this into 1.4 alongside 1.4.10, without the short circuit of course. |
||
|
|
||
| #### Requirement 1.7.8 | ||
|
|
||
| > Implementations **SHOULD** propagate the `error code` returned from any provider lifecycle methods. | ||
|
|
||
| The SDK ensures that if the provider's lifecycle methods terminate with an `error code`, that error code is included in any associated error events and returned/thrown errors/exceptions. | ||
|
|
||
| see: [error codes](../types.md#error-code) | ||
|
|
||
| #### Requirement 1.7.9 | ||
| #### Requirement 1.7.7 | ||
|
|
||
| > The client's `provider status` accessor **MUST** indicate `NOT_READY` once the `shutdown` function of the associated provider terminates. | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -205,7 +205,7 @@ class MyProvider implements Provider { | |||||
| If a provider is unable to start up correctly, it should indicate abnormal execution by throwing an exception, returning an error, or otherwise indicating so by means idiomatic to the implementation language. | ||||||
| If the error is irrecoverable (perhaps due to bad credentials or invalid configuration) the `PROVIDER_FATAL` error code should be used. | ||||||
|
|
||||||
| see: [error codes](../types.md#error-code) | ||||||
| see: [error codes](../types.md#error-code), [provider status](#28-provider-status) | ||||||
|
|
||||||
| ### 2.5. Shutdown | ||||||
|
|
||||||
|
|
@@ -269,6 +269,8 @@ class MyProvider implements Provider { | |||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| see: [provider status](#28-provider-status) | ||||||
|
|
||||||
| Providers may maintain remote connections, timers, threads or other constructs that need to be appropriately disposed of. | ||||||
| Provider authors may implement a `shutdown` function to perform relevant clean-up actions. | ||||||
| Alternatively, implementations might leverage language idioms such as auto-disposable interfaces or some means of cancellation signal propagation to allow for graceful shutdown. | ||||||
|
|
@@ -305,3 +307,52 @@ The track function performs side effects required to record the `tracking event` | |||||
| Providers should be careful to complete any communication or flush any relevant uncommitted tracking data before they shut down. | ||||||
|
|
||||||
| See [shutdown](#25-shutdown). | ||||||
|
|
||||||
| ### 2.8. Provider status | ||||||
|
|
||||||
| [](https://github.com/open-feature/spec/tree/main/specification#hardening) | ||||||
|
|
||||||
| The SDK derives provider status from events emitted by the provider. | ||||||
| Providers signal all state transitions by emitting the appropriate event; the SDK updates its internal status accordingly and runs associated handlers. | ||||||
|
|
||||||
| Providers that do not define lifecycle methods or an event emission mechanism cannot emit events by design; see Condition 2.8.5. | ||||||
| Requirements 2.8.1-2.8.4 apply only to providers that define lifecycle methods and an event emission mechanism. | ||||||
|
Comment on lines
+318
to
+319
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can a provider without lifecycle methods but an event emission mechanism emit events? |
||||||
|
|
||||||
| see: [provider lifecycle management](./01-flag-evaluation.md#17-provider-lifecycle-management), [provider events](./05-events.md#51-provider-events) | ||||||
|
|
||||||
| #### Requirement 2.8.1 | ||||||
|
|
||||||
| > The provider **MUST** emit an event to signal each status transition, including transitions resulting from lifecycle methods (`initialize`, `on context changed`) and spontaneous transitions. | ||||||
|
|
||||||
| Providers must not rely on the SDK to infer status from lifecycle method return values. | ||||||
| Instead, the provider emits the appropriate event (e.g. `PROVIDER_READY` after successful initialization) to signal each transition. | ||||||
|
|
||||||
| see: [provider events](./05-events.md#51-provider-events), [provider event types](../types.md#provider-events) | ||||||
|
|
||||||
| #### Requirement 2.8.2 | ||||||
|
|
||||||
| > The provider **MUST** emit `PROVIDER_READY` if its `initialize` function terminates normally. | ||||||
|
|
||||||
| #### Requirement 2.8.3 | ||||||
|
|
||||||
| > The provider **MUST** emit `PROVIDER_ERROR` if its `initialize` function terminates abnormally. | ||||||
|
|
||||||
| If the error is irrecoverable, the error code must indicate `PROVIDER_FATAL`. | ||||||
|
Comment on lines
+338
to
+340
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. in Should we add a requirement here that says that providers |
||||||
|
|
||||||
| see: [error codes](../types.md#error-code) | ||||||
|
|
||||||
| #### Requirement 2.8.4 | ||||||
|
|
||||||
| > The provider **MUST** emit `PROVIDER_CONTEXT_CHANGED` if its `on context changed` function terminates normally, and `PROVIDER_ERROR` if it terminates abnormally. | ||||||
|
|
||||||
| see: [provider context reconciliation](#26-provider-context-reconciliation) | ||||||
|
|
||||||
| #### Condition 2.8.5 | ||||||
|
|
||||||
| > The provider does not define an `initialize` function or an event emission mechanism. | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above I am confused by the
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also a bit confused. |
||||||
|
|
||||||
| ##### Conditional Requirement 2.8.5.1 | ||||||
|
|
||||||
| > The SDK **MUST** treat such providers as `READY` from registration and **MUST** run `PROVIDER_READY` handlers on their behalf. | ||||||
|
|
||||||
| Such providers cannot emit their own events by design. | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit (and very much a question to learn): I'm not very familiar with how these work, but assuming that code/tests are being generated from them. Is this re-numbering a good idea?