diff --git a/specification.json b/specification.json index eb3ed089..78bbf147 100644 --- a/specification.json +++ b/specification.json @@ -287,6 +287,70 @@ "RFC 2119 keyword": "MUST", "children": [] }, + { + "id": "Requirement 1.7.1", + "machine_id": "requirement_1_7_1", + "content": "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`.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Condition 1.7.2", + "machine_id": "condition_1_7_2", + "content": "The implementation uses the static-context paradigm.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 1.7.2.1", + "machine_id": "conditional_requirement_1_7_2_1", + "content": "In addition to `NOT_READY`, `READY`, `STALE`, or `ERROR`, the `provider status` accessor must support possible value `RECONCILING`.", + "RFC 2119 keyword": null, + "children": [] + } + ] + }, + { + "id": "Requirement 1.7.3", + "machine_id": "requirement_1_7_3", + "content": "The client's `provider status` accessor MUST indicate `READY` if the `initialize` function of the associated provider terminates normally.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.7.4", + "machine_id": "requirement_1_7_4", + "content": "The client's `provider status` accessor MUST indicate `ERROR` if the `initialize` function of the associated provider terminates abnormally.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.7.5", + "machine_id": "requirement_1_7_5", + "content": "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`.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.7.6", + "machine_id": "requirement_1_7_6", + "content": "The client MUST default, run error hooks, and indicate an error if flag resolution is attempted while the provider is in `NOT_READY`.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.7.7", + "machine_id": "requirement_1_7_7", + "content": "The client MUST default, run error hooks, and indicate an error if flag resolution is attempted while the provider is in `PROVIDER_FATAL`.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 1.7.8", + "machine_id": "requirement_1_7_8", + "content": "Implementations SHOULD propagate the `error code` returned from any provider lifecycle methods.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, { "id": "Requirement 2.1.1", "machine_id": "requirement_2_1_1", @@ -409,32 +473,19 @@ "children": [] }, { - "id": "Requirement 2.4.2", - "machine_id": "requirement_2_4_2", - "content": "The `provider` MAY define a `status` field/accessor which indicates the readiness of the provider, with possible values `NOT_READY`, `READY`, `STALE`, or `ERROR`.", - "RFC 2119 keyword": "MAY", - "children": [] - }, - { - "id": "Requirement 2.4.3", - "machine_id": "requirement_2_4_3", - "content": "The provider MUST set its `status` field/accessor to `READY` if its `initialize` function terminates normally.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 2.4.4", - "machine_id": "requirement_2_4_4", - "content": "The provider MUST set its `status` field to `ERROR` if its `initialize` function terminates abnormally.", - "RFC 2119 keyword": "MUST", - "children": [] - }, - { - "id": "Requirement 2.4.5", - "machine_id": "requirement_2_4_5", - "content": "The provider SHOULD indicate an error if flag resolution is attempted before the provider is ready.", - "RFC 2119 keyword": "SHOULD", - "children": [] + "id": "Condition 2.4.2", + "machine_id": "condition_2_4_2", + "content": "The provider defines an `initialize` function.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 2.4.2.1", + "machine_id": "conditional_requirement_2_4_2_1", + "content": "If the provider's `initialize` function fails to render the provider ready to evaluate flags, it SHOULD abnormally terminate.", + "RFC 2119 keyword": "SHOULD", + "children": [] + } + ] }, { "id": "Requirement 2.5.1", @@ -453,7 +504,7 @@ { "id": "Requirement 2.6.1", "machine_id": "requirement_2_6_1", - "content": "The provider MAY define an `on context changed` handler, which takes an argument for the previous context and the newly set context, in order to respond to an evaluation context change.", + "content": "The provider MAY define an `on context changed` function, which takes an argument for the previous context and the newly set context, in order to respond to an evaluation context change.", "RFC 2119 keyword": "MAY", "children": [] }, @@ -552,14 +603,14 @@ { "id": "Conditional Requirement 3.2.4.1", "machine_id": "conditional_requirement_3_2_4_1", - "content": "When the global `evaluation context` is set, the `on context changed` handler MUST run.", + "content": "When the global `evaluation context` is set, the `on context changed` function MUST run.", "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Conditional Requirement 3.2.4.2", "machine_id": "conditional_requirement_3_2_4_2", - "content": "When the `evaluation context` for a specific provider is set, the `on context changed` handler MUST only run on the associated provider.", + "content": "When the `evaluation context` for a specific provider is set, the `on context changed` function MUST only run on the associated provider.", "RFC 2119 keyword": "MUST", "children": [] } @@ -881,6 +932,13 @@ "RFC 2119 keyword": "SHOULD", "children": [] }, + { + "id": "Requirement 5.1.5", + "machine_id": "requirement_5_1_5", + "content": "`PROVIDER_ERROR` events SHOULD populate the `provider event details`'s `error code` field.", + "RFC 2119 keyword": "SHOULD", + "children": [] + }, { "id": "Requirement 5.2.1", "machine_id": "requirement_5_2_1", @@ -960,25 +1018,32 @@ { "id": "Conditional Requirement 5.3.4.1", "machine_id": "conditional_requirement_5_3_4_1", - "content": "When the provider's `on context changed` is called, the provider MAY emit the `PROVIDER_STALE` event, and transition to the `STALE` state.", - "RFC 2119 keyword": "MAY", + "content": "While the provider's `on context changed` function is executing, associated `RECONCILING` handlers MUST run.", + "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Conditional Requirement 5.3.4.2", "machine_id": "conditional_requirement_5_3_4_2", - "content": "If the provider's `on context changed` function terminates normally, associated `PROVIDER_CONTEXT_CHANGED` handlers MUST run.", + "content": "If the provider's `on context changed` function terminates normally, and no other invocations have yet to terminate, associated `PROVIDER_CONTEXT_CHANGED` handlers MUST run.", "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Conditional Requirement 5.3.4.3", "machine_id": "conditional_requirement_5_3_4_3", - "content": "If the provider's `on context changed` function terminates abnormally, associated `PROVIDER_ERROR` handlers MUST run.", + "content": "If the provider's `on context changed` function terminates abnormally, and no other invocations have yet to terminate, associated `PROVIDER_ERROR` handlers MUST run.", "RFC 2119 keyword": "MUST", "children": [] } ] + }, + { + "id": "Requirement 5.3.5", + "machine_id": "requirement_5_3_5", + "content": "If the provider emits an event, the value of the client's `provider status` MUST be updated accordingly.", + "RFC 2119 keyword": "MUST", + "children": [] } ] } \ No newline at end of file diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index 25e147c3..493b1e07 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -401,3 +401,109 @@ The global API object might expose a `shutdown` function, which will call the re Alternatively, implementations might leverage language idioms such as auto-disposable interfaces or some means of cancellation signal propagation to allow for graceful shutdown. see: [`shutdown`](./02-providers.md#25-shutdown) + +### 1.7. Provider Lifecycle Management + +The implementation maintains an internal representation of the state of configured providers, tracking the lifecycle of each provider. +This state of the provider is exposed on associated `clients`. + +The diagram below illustrates the possible states and transitions of the `state` field for a provider during the provider lifecycle. + +```mermaid +--- +title: Provider lifecycle +--- +stateDiagram-v2 + direction LR + [*] --> NOT_READY + NOT_READY --> READY:initialize() + NOT_READY --> ERROR:initialize() + NOT_READY --> FATAL:initialize() + FATAL --> [*] + READY --> ERROR:* + ERROR --> READY:* + READY --> STALE:* + STALE --> READY:* + STALE --> ERROR:* + READY --> NOT_READY:shutdown() + STALE --> NOT_READY:shutdown() + ERROR --> NOT_READY:shutdown() + READY --> RECONCILING:::client:setContext() + RECONCILING:::client --> READY + RECONCILING:::client --> ERROR + + classDef client fill:#888 +``` + +\* transitions occurring when associated events are spontaneously emitted from the provider + + only defined in static-context (client-side) paradigm + +> [!NOTE] +> Only SDKs implementing the [static context (client-side) paradigm](../glossary.md#static-context-paradigm) define `RECONCILING` to facilitate [context reconciliation](./02-providers.md#26-provider-context-reconciliation). + +#### Requirement 1.7.1 + +> 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. + +see [provider status](../types.md#provider-status) + +#### Condition 1.7.2 + +[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental) + +> The implementation uses the static-context paradigm. + +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 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. + +Once the provider has initialized, the `provider status` should indicate the provider is ready to be used to evaluate flags. + +#### Requirement 1.7.4 + +> The client's `provider status` accessor **MUST** indicate `ERROR` if the `initialize` function of the associated provider terminates abnormally. + +If the provider has failed to initialize, the `provider status` should indicate the provider is in an error state. + +#### 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`. + +If the provider has failed to initialize, the `provider status` should indicate the provider is in an error state. + +#### 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 `PROVIDER_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) + +#### 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) \ No newline at end of file diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index 0ff71b0d..b540cdb3 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -188,53 +188,18 @@ class MyProvider implements Provider { } ``` -#### Requirement 2.4.2 +#### Condition 2.4.2 -> The `provider` **MAY** define a `status` field/accessor which indicates the readiness of the provider, with possible values `NOT_READY`, `READY`, `STALE`, or `ERROR`. +> The provider defines an `initialize` function. -Providers without this field can be assumed to be ready immediately. +##### Conditional Requirement 2.4.2.1 -The diagram below illustrates the possible states and transitions of the `status` fields. +> If the provider's `initialize` function fails to render the provider ready to evaluate flags, it **SHOULD** abnormally terminate. -```mermaid ---- -title: Provider State ---- -stateDiagram-v2 - direction LR - [*] --> NOT_READY - NOT_READY --> READY:initialize - READY --> ERROR - ERROR --> READY - READY --> STALE - STALE --> READY - STALE --> ERROR - READY --> NOT_READY:shutdown - STALE --> NOT_READY:shutdown - ERROR --> NOT_READY:shutdown -``` - -see [provider status](../types.md#provider-status) - -#### Requirement 2.4.3 - -> The provider **MUST** set its `status` field/accessor to `READY` if its `initialize` function terminates normally. - -If the provider supports the `status` field/accessor and initialization succeeds, setting the `status` to `READY` indicates that the provider is initialized and flag evaluation is proceeding normally. - -#### Requirement 2.4.4 - -> The provider **MUST** set its `status` field to `ERROR` if its `initialize` function terminates abnormally. - -If the provider supports the `status` field/accessor and initialization fails, setting the `status` to `ERROR` indicates the provider is in an error state. If the error is transient in nature (ex: a connectivity failure of some kind) the provider can attempt to resolve this state automatically. - -#### Requirement 2.4.5 - -> The provider **SHOULD** indicate an error if flag resolution is attempted before the provider is ready. - -It's recommended to set an informative `error code`, such as `PROVIDER_NOT_READY` if evaluation in attempted before the provider is initialized. +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](https://openfeature.dev/specification/types#error-code) +see: [error codes](../types.md#error-code) ### 2.5. Shutdown @@ -266,14 +231,14 @@ see: [initialization](#24-initialization) [![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental) -Static-context focused providers may need a mechanism to understand when their cache of evaluated flags must be invalidated or updated. An `on context changed` handler can be defined which performs whatever operations are needed to reconcile the evaluated flags with the new context. +Static-context focused providers may need a mechanism to understand when their cache of evaluated flags must be invalidated or updated. An `on context changed` function can be defined which performs whatever operations are needed to reconcile the evaluated flags with the new context. #### Requirement 2.6.1 -> The provider **MAY** define an `on context changed` handler, which takes an argument for the previous context and the newly set context, in order to respond to an evaluation context change. +> The provider **MAY** define an `on context changed` function, which takes an argument for the previous context and the newly set context, in order to respond to an evaluation context change. Especially in static-context implementations, providers and underlying SDKs may maintain state for a particular context. -The `on context changed` handler provides a mechanism to update this state, often by re-evaluating flags in bulk with respect to the new context. +The `on context changed` function provides a mechanism to update this state, often by re-evaluating flags in bulk with respect to the new context. ```java // MyProvider implementation of the onContextChanged function defined in Provider diff --git a/specification/sections/03-evaluation-context.md b/specification/sections/03-evaluation-context.md index 9a7641c4..478d3c78 100644 --- a/specification/sections/03-evaluation-context.md +++ b/specification/sections/03-evaluation-context.md @@ -124,15 +124,15 @@ see: [static-context paradigm](../glossary.md#static-context-paradigm) ##### Conditional Requirement 3.2.4.1 -> When the global `evaluation context` is set, the `on context changed` handler **MUST** run. +> When the global `evaluation context` is set, the `on context changed` function **MUST** run. -The SDK implementation must run the `on context changed` handler on all registered provider that use the global `evaluation context` whenever it is mutated. +The SDK implementation must run the `on context changed` function on all registered provider that use the global `evaluation context` whenever it is mutated. ##### Conditional Requirement 3.2.4.2 -> When the `evaluation context` for a specific provider is set, the `on context changed` handler **MUST** only run on the associated provider. +> When the `evaluation context` for a specific provider is set, the `on context changed` function **MUST** only run on the associated provider. -The SDK implementation must run the `on context changed` handler only on the provider that is scoped to the mutated `evaluation context`. +The SDK implementation must run the `on context changed` function only on the provider that is scoped to the mutated `evaluation context`. ### 3.3 Context Propagation diff --git a/specification/sections/05-events.md b/specification/sections/05-events.md index a7a56d73..a6ebc5ad 100644 --- a/specification/sections/05-events.md +++ b/specification/sections/05-events.md @@ -28,9 +28,17 @@ graph > The `provider` **MAY** define a mechanism for signaling the occurrence of one of a set of events, including `PROVIDER_READY`, `PROVIDER_ERROR`, `PROVIDER_CONFIGURATION_CHANGED` and `PROVIDER_STALE`, with a `provider event details` payload. +Providers cannot emit `PROVIDER_CONTEXT_CHANGED` or `PROVIDER_RECONCILING` event. +These are emitted only by the SDK during context reconciliation. + If available, native event-emitter or observable/observer language constructs can be used. -see: [provider event types](../types.md#provider-events), [`event details`](../types.md#provider-event-details). +When a provider is unable to evaluate flags (perhaps due to loss of connection with a remote service) the provider can signal this by emitting a `PROVIDER_ERROR` event. +When it recovers, it can emit a `PROVIDER_READY` event. +If the error state is irrecoverable, the `PROVIDER_FATAL` error code can be used. +If a provider caches rules-sets or previously evaluated flags, and such states cannot be considered up-to-date, the provider can signal this by emitting a `PROVIDER_STALE` event. + +see: [provider event types](../types.md#provider-events), [`event details`](../types.md#provider-event-details), [events handlers and context reconciliation](#event-handlers-and-context-reconciliation) #### Requirement 5.1.2 @@ -54,6 +62,12 @@ The error message field should contain an informative message as to the nature o See [event metadata](../types.md#provider-event-details) +#### Requirement 5.1.5 + +> `PROVIDER_ERROR` events **SHOULD** populate the `provider event details`'s `error code` field. + +See [event metadata](../types.md#provider-event-details) + ### 5.2. Event handlers #### Requirement 5.2.1 @@ -147,10 +161,13 @@ See [provider initialization](./02-providers.md#24-initialization), [setting a p ### Event handlers and context reconciliation -Providers built to conform to the static context paradigm feature an additional `PROVIDER_CONTEXT_CHANGED` event, which is used to signal that the global context has been changed, and flags should be re-evaluated. +Providers built to conform to the static context paradigm feature two additional events: `PROVIDER_RECONCILING` and `PROVIDER_CONTEXT_CHANGED`. +When the provider is reconciling its internal state (the `on context changed` function is running and not yet terminated), the SDK emits `PROVIDER_RECONCILING` and transitions the provider into state `RECONCILING`. +This can be particularly useful for displaying loading indicators while the [evaluation context](./03-evaluation-context.md) is being reconciled. + +If the `on context changed` function terminates normally, the SDK emits (`PROVIDER_CONTEXT_CHANGED`) and transitions the provider into the `READY` state, otherwise it emits `PROVIDER_ERROR` and transitions the provider into `ERROR` state. +The `PROVIDER_CONTEXT_CHANGED` is used to signal that the associated context has been changed, and flags should be re-evaluated. This can be particularly useful for triggering UI repaints in multiple components when one component updates the [evaluation context](./03-evaluation-context.md). -SDK implementations automatically fire the the `PROVIDER_CONTEXT_CHANGED` events if the `on context changed` handler terminates normally (and `PROVIDER_ERROR` events otherwise). -Optionally, some providers may transition to the `STALE` state while their associated context is waiting to be reconciled, since this may involve asynchronous operations such as network calls. ```mermaid --- @@ -158,13 +175,14 @@ title: Provider context reconciliation --- stateDiagram-v2 direction TB - READY --> READY:emit(PROVIDER_CONTEXT_CHANGED) - ERROR --> READY:emit(PROVIDER_READY) - READY --> STALE:emit(PROVIDER_STALE) - STALE --> READY:emit(PROVIDER_CONTEXT_CHANGED) - STALE --> ERROR:emit(PROVIDER_ERROR) + READY --> READY:emit(PROVIDER_CONTEXT_CHANGED)* + READY --> RECONCILING:emit(PROVIDER_RECONCILING) + RECONCILING --> READY:emit(PROVIDER_CONTEXT_CHANGED) + RECONCILING --> ERROR:emit(PROVIDER_ERROR) ``` +\* Implementations may allow for providers to reconcile synchronously, in which case no `PROVIDER_RECONCILING` event is emitted. + #### Condition 5.3.4 [![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental) @@ -175,26 +193,45 @@ see: [static-context paradigm](../glossary.md#static-context-paradigm) ##### Conditional Requirement 5.3.4.1 -> When the provider's `on context changed` is called, the provider **MAY** emit the `PROVIDER_STALE` event, and transition to the `STALE` state. +> While the provider's `on context changed` function is executing, associated `RECONCILING` handlers **MUST** run. -Some providers cache evaluated flags, and re-evaluate them when the context is changed. -In these cases, the provider may signal its cache is invalid with the `PROVIDER_STALE` event and the `STALE` provider state. +The implementation must run any `RECONCILING` handlers associated with the provider while the provider is reconciling its state. +In languages with asynchronous semantics, the emission of this event can be skipped if the `on context changed` function of the provider in question executes synchronously for a given provider, no other operations can take place while it runs. -see: [provider event types](../types.md#provider-events), [provider events](#51-provider-events), context, [provider context reconciliation](02-providers.md#26-provider-context-reconciliation) +see: [provider event types](../types.md#provider-events), [provider events](#51-provider-events), [provider context reconciliation](02-providers.md#26-provider-context-reconciliation) ##### Conditional Requirement 5.3.4.2 -> If the provider's `on context changed` function terminates normally, associated `PROVIDER_CONTEXT_CHANGED` handlers **MUST** run. +> If the provider's `on context changed` function terminates normally, and no other invocations have yet to terminate, associated `PROVIDER_CONTEXT_CHANGED` handlers **MUST** run. The implementation must run any `PROVIDER_CONTEXT_CHANGED` handlers associated with the provider after the provider has reconciled its state and returned from the `on context changed` function. The `PROVIDER_CONTEXT_CHANGED` is not emitted from the provider itself; the SDK implementation must run the `PROVIDER_CONTEXT_CHANGED` handlers if the `on context changed` function terminates normally. - -see: [provider event types](../types.md#provider-events), [provider events](#51-provider-events), context, [provider context reconciliation](02-providers.md#26-provider-context-reconciliation) +It's possible that the `on context changed` function is invoked simultaneously or in quick succession; in this case the SDK will only run the `PROVIDER_CONTEXT_CHANGED` handlers after all reentrant invocations have terminated, and the last to terminate was successful (terminated normally). +see: [provider event types](../types.md#provider-events), [provider events](#51-provider-events), [provider context reconciliation](02-providers.md#26-provider-context-reconciliation) ##### Conditional Requirement 5.3.4.3 -> If the provider's `on context changed` function terminates abnormally, associated `PROVIDER_ERROR` handlers **MUST** run. +> If the provider's `on context changed` function terminates abnormally, and no other invocations have yet to terminate, associated `PROVIDER_ERROR` handlers **MUST** run. The `PROVIDER_ERROR` is not emitted from the provider itself; the SDK implementation must run the `PROVIDER_ERROR` handlers if the `on context changed` throws or otherwise signals an error. +It's possible that the `on context changed` function is invoked simultaneously or in quick succession; in this case the SDK will only run the `PROVIDER_ERROR` handlers after all reentrant invocations have terminated, and the last to terminate was unsuccessful (terminated abnormally). + +see: [provider event types](../types.md#provider-events), [provider events](#51-provider-events), [provider context reconciliation](02-providers.md#26-provider-context-reconciliation) + +#### Requirement 5.3.5 + +> If the provider emits an event, the value of the client's `provider status` **MUST** be updated accordingly. + +Some providers may emit events spontaneously, based on changes in their internal state (connections, caches, etc). +The SDK must update it's internal representation of the provider's state accordingly: + +| Event | Associated Status | +| -------------------------------- | ------------------------------------------------------- | +| `PROVIDER_READY` | `READY` | +| `PROVIDER_STALE` | `STALE` | +| `PROVIDER_ERROR` | `ERROR` | +| `PROVIDER_CONFIGURATION_CHANGED` | N/A (provider remains in its current state) | +| `PROVIDER_CONTEXT_CHANGED` | N/A (only emitted by SDK during context reconciliation) | +| `PROVIDER_RECONCILING` | N/A (only emitted by SDK during context reconciliation) | -see: [provider event types](../types.md#provider-events), [provider events](#51-provider-events), context, [provider context reconciliation](02-providers.md#26-provider-context-reconciliation) \ No newline at end of file +see: [provider lifecycle management](01-flag-evaluation.md#17-provider-lifecycle-management) \ No newline at end of file diff --git a/specification/types.md b/specification/types.md index e69f283d..57189c80 100644 --- a/specification/types.md +++ b/specification/types.md @@ -110,6 +110,7 @@ An enumerated error code represented idiomatically in the implementation languag | TYPE_MISMATCH | The type of the flag value does not match the expected type. | | TARGETING_KEY_MISSING | The provider requires a targeting key and one was not provided in the `evaluation context`. | | INVALID_CONTEXT | The `evaluation context` does not meet provider requirements. | +| PROVIDER_FATAL | The provider has entered an irrecoverable error state. | | GENERAL | The error was for a reason not enumerated above. | ### Evaluation Options @@ -128,12 +129,15 @@ This structure is populated by a provider for use by an [Application Author](./g An enumeration of possible provider states. -| Status | Explanation | -| --------- | --------------------------------------------------------------------------------------------------- | -| NOT_READY | The provider has not been initialized. | -| READY | The provider has been initialized, and is able to reliably resolve flag values. | -| ERROR | The provider is initialized but is not able to reliably resolve flag values. | -| STALE | The provider's cached state is no longer valid and may not be up-to-date with the source of truth. | +| Status | Explanation | +| ------------ | -------------------------------------------------------------------------------------------------- | +| NOT_READY | The provider has not been initialized. | +| READY | The provider has been initialized, and is able to reliably resolve flag values. | +| ERROR | The provider is initialized but is not able to reliably resolve flag values. | +| STALE | The provider's cached state is no longer valid and may not be up-to-date with the source of truth. | +| RECONCILING* | The provider is reconciling its state with a context change. | + +\* [static context (client-side) paradigm](./glossary.md#static-context-paradigm) only ### Provider Event Details @@ -150,6 +154,7 @@ A structure defining an event payload, including: - provider name (string, required) - flags changed (string[], optional) - message (string, optional) +- error code ([error code](#error-code), optional) - event metadata ([event metadata](#event-metadata)) ### Event Metadata @@ -161,13 +166,14 @@ It supports definition of arbitrary properties, with keys of type `string`, and An enumeration of provider events. -| Event | Explanation | -| ------------------------------ | ------------------------------------------------------------------------------------------------------------ | -| PROVIDER_READY | The provider is ready to perform flag evaluations. | -| PROVIDER_ERROR | The provider signalled an error. | -| PROVIDER_CONFIGURATION_CHANGED | A change was made to the backend flag configuration. | -| PROVIDER_STALE | The provider's cached state is no longer valid and may not be up-to-date with the source of truth. | -| PROVIDER_CONTEXT_CHANGED* | The context associated with the provider has changed, and the provider has reconciled it's associated state. | +| Event | Explanation | +| ------------------------------ | ------------------------------------------------------------------------------------------------------------------- | +| PROVIDER_READY | The provider is ready to perform flag evaluations. | +| PROVIDER_ERROR | The provider signalled an error. | +| PROVIDER_CONFIGURATION_CHANGED | A change was made to the backend flag configuration. | +| PROVIDER_STALE | The provider's cached state is no longer valid and may not be up-to-date with the source of truth. | +| PROVIDER_RECONCILING* | The context associated with the provider has changed, and the provider has not yet reconciled its associated state. | +| PROVIDER_CONTEXT_CHANGED* | The context associated with the provider has changed, and the provider has reconciled its associated state. | \* [static context (client-side) paradigm](./glossary.md#static-context-paradigm) only