diff --git a/specification.json b/specification.json index 35ce7efc2..0a8169060 100644 --- a/specification.json +++ b/specification.json @@ -310,8 +310,8 @@ { "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, + "content": "In addition to `NOT_READY`, `READY`, `STALE`, `ERROR`, or `FATAL`, the `provider status` accessor MUST support possible value `RECONCILING`.", + "RFC 2119 keyword": "MUST", "children": [] } ] @@ -319,48 +319,34 @@ { "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.", + "content": "The client's `provider status` accessor MUST indicate `READY` after the provider emits `PROVIDER_READY` following initialization.", "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.", + "content": "The client's `provider status` accessor MUST indicate `ERROR` after the provider emits `PROVIDER_ERROR` following initialization.", "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`.", + "content": "The client's `provider status` accessor MUST indicate `FATAL` after the provider emits `PROVIDER_ERROR` with error code `PROVIDER_FATAL` following initialization.", "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 `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 1.7.9", - "machine_id": "requirement_1_7_9", + "id": "Requirement 1.7.7", + "machine_id": "requirement_1_7_7", "content": "The client's `provider status` accessor MUST indicate `NOT_READY` once the `shutdown` function of the associated provider terminates.", "RFC 2119 keyword": "MUST", "children": [] @@ -564,6 +550,49 @@ "RFC 2119 keyword": "MAY", "children": [] }, + { + "id": "Requirement 2.8.1", + "machine_id": "requirement_2_8_1", + "content": "The provider MUST emit an event to signal each status transition, including transitions resulting from lifecycle methods (`initialize`, `on context changed`) and spontaneous transitions.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 2.8.2", + "machine_id": "requirement_2_8_2", + "content": "The provider MUST emit `PROVIDER_READY` if its `initialize` function terminates normally.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 2.8.3", + "machine_id": "requirement_2_8_3", + "content": "The provider MUST emit `PROVIDER_ERROR` if its `initialize` function terminates abnormally.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Requirement 2.8.4", + "machine_id": "requirement_2_8_4", + "content": "The provider MUST emit `PROVIDER_CONTEXT_CHANGED` if its `on context changed` function terminates normally, and `PROVIDER_ERROR` if it terminates abnormally.", + "RFC 2119 keyword": "MUST", + "children": [] + }, + { + "id": "Condition 2.8.5", + "machine_id": "condition_2_8_5", + "content": "The provider does not define an `initialize` function or an event emission mechanism.", + "RFC 2119 keyword": null, + "children": [ + { + "id": "Conditional Requirement 2.8.5.1", + "machine_id": "conditional_requirement_2_8_5_1", + "content": "The SDK MUST treat such providers as `READY` from registration and MUST run `PROVIDER_READY` handlers on their behalf.", + "RFC 2119 keyword": "MUST", + "children": [] + } + ] + }, { "id": "Requirement 3.1.1", "machine_id": "requirement_3_1_1", @@ -984,8 +1013,8 @@ { "id": "Requirement 5.1.1", "machine_id": "requirement_5_1_1", - "content": "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.", - "RFC 2119 keyword": "MAY", + "content": "The `feature provider` interface MUST define a mechanism for signaling the occurrence of one of a set of events, including `PROVIDER_READY`, `PROVIDER_ERROR`, `PROVIDER_CONFIGURATION_CHANGED`, `PROVIDER_STALE`, `PROVIDER_RECONCILING`, and `PROVIDER_CONTEXT_CHANGED`, with a `provider event details` payload.", + "RFC 2119 keyword": "MUST", "children": [] }, { @@ -1068,14 +1097,14 @@ { "id": "Requirement 5.3.1", "machine_id": "requirement_5_3_1", - "content": "If the provider's `initialize` function terminates normally, `PROVIDER_READY` handlers MUST run.", + "content": "When the provider emits `PROVIDER_READY`, associated `PROVIDER_READY` handlers MUST run.", "RFC 2119 keyword": "MUST", "children": [] }, { "id": "Requirement 5.3.2", "machine_id": "requirement_5_3_2", - "content": "If the provider's `initialize` function terminates abnormally, `PROVIDER_ERROR` handlers MUST run.", + "content": "When the provider emits `PROVIDER_ERROR`, associated `PROVIDER_ERROR` handlers MUST run.", "RFC 2119 keyword": "MUST", "children": [] }, @@ -1118,7 +1147,7 @@ { "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 to the status associated with that event **before** the SDK invokes any event handlers for that event, so that handlers observe a consistent status.", + "content": "When a provider emits an event, the SDK MUST update the `provider status` to the status associated with that event **before** invoking any event handlers for that event, so that handlers observe a consistent status.", "RFC 2119 keyword": "MUST", "children": [] }, diff --git a/specification/README.md b/specification/README.md index c39658d57..6772899d1 100644 --- a/specification/README.md +++ b/specification/README.md @@ -21,6 +21,7 @@ sidebar_position: 0 - [Appendix B: Gherkin Suites](./appendix-b-gherkin-suites.md) - [Appendix C: OFREP](./appendix-c/index.md) - [Appendix D: Observability](./appendix-d-observability.md) +- [Appendix E: Migrations](./appendix-e-migrations.md) ## Conformance diff --git a/specification/appendix-e-migrations.md b/specification/appendix-e-migrations.md new file mode 100644 index 000000000..fa69367de --- /dev/null +++ b/specification/appendix-e-migrations.md @@ -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. diff --git a/specification/sections/01-flag-evaluation.md b/specification/sections/01-flag-evaluation.md index cb47dffe4..9d90ce9e4 100644 --- a/specification/sections/01-flag-evaluation.md +++ b/specification/sections/01-flag-evaluation.md @@ -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) - -#### 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. diff --git a/specification/sections/02-providers.md b/specification/sections/02-providers.md index baa7d87e3..f3356524b 100644 --- a/specification/sections/02-providers.md +++ b/specification/sections/02-providers.md @@ -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 + +[![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](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. + +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`. + +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. + +##### 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. diff --git a/specification/sections/05-events.md b/specification/sections/05-events.md index 17af33d41..f00fc3461 100644 --- a/specification/sections/05-events.md +++ b/specification/sections/05-events.md @@ -31,10 +31,11 @@ see: [domain](../glossary.md#domain) #### Requirement 5.1.1 -> 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. +> The `feature provider` interface **MUST** define a mechanism for signaling the occurrence of one of a set of events, including `PROVIDER_READY`, `PROVIDER_ERROR`, `PROVIDER_CONFIGURATION_CHANGED`, `PROVIDER_STALE`, `PROVIDER_RECONCILING`, and `PROVIDER_CONTEXT_CHANGED`, with a `provider event details` payload. -Providers cannot emit `PROVIDER_CONTEXT_CHANGED` or `PROVIDER_RECONCILING` events. -These are emitted only by the SDK during context reconciliation. +Providers must emit events to signal all state transitions, including those resulting from lifecycle methods (initialize, reconciliation). +The SDK derives provider status from these events. +Providers without lifecycle methods or an event emission mechanism cannot emit events by design; see [Condition 2.8.5](./02-providers.md#condition-285). If available, native event-emitter or observable/observer language constructs can be used. @@ -138,22 +139,21 @@ This means that the order of provider configuration and event handler addition i ### Event handlers and initialization -Though providers themselves need not implement events, the `flag evaluation API` uses events to convey relevant state changes during configuration and initialization. -Implementations automatically emit `PROVIDER_READY` or `PROVIDER_ERROR` events depending on the outcome of the `initialize` function, if the provider has implemented one (if none is implemented, `PROVIDER_READY` runs unconditionally). +The provider signals successful or failed initialization by emitting `PROVIDER_READY` or `PROVIDER_ERROR` events respectively. _Application authors_ and _application integrators_ use these events to wait for proper initialization of the provider and to do basic monitoring and error handling. #### Requirement 5.3.1 -> If the provider's `initialize` function terminates normally, `PROVIDER_READY` handlers **MUST** run. +> When the provider emits `PROVIDER_READY`, associated `PROVIDER_READY` handlers **MUST** run. See [provider initialization](./02-providers.md#24-initialization) and [setting a provider](./01-flag-evaluation.md#setting-a-provider). #### Requirement 5.3.2 -> If the provider's `initialize` function terminates abnormally, `PROVIDER_ERROR` handlers **MUST** run. +> When the provider emits `PROVIDER_ERROR`, associated `PROVIDER_ERROR` handlers **MUST** run. A failed initialization could represent an unrecoverable error, such as bad credentials or a missing file. -If a failed initialization could also represent a transient error. +A failed initialization could also represent a transient error. A provider which maintains a persistent connection to a remote `flag management system` may attempt to reconnect, and emit `PROVIDER_READY` after a failed initialization. See [provider initialization](./02-providers.md#24-initialization) and [setting a provider](./01-flag-evaluation.md#setting-a-provider). @@ -166,16 +166,17 @@ Handlers may be attached at any point in the application lifecycle. Handlers should run immediately if the provider is already in the associated state. For instance, _application authors_ may attach readiness handlers to be confident that the system is ready to evaluate flags. If such handlers are attached after the provider underlying the client has already been initialized, they should run immediately. +In multi-threaded environments, implementations should ensure that running a handler immediately upon registration does not result in out-of-order event processing (e.g. by holding a lock on the event queue). See [provider initialization](./02-providers.md#24-initialization), [setting a provider](./01-flag-evaluation.md#setting-a-provider). ### Event handlers and context reconciliation 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 transitions the provider into state `RECONCILING` and then emits `PROVIDER_RECONCILING`. +When the provider is reconciling its internal state (the `on context changed` function is running and not yet terminated), the provider emits `PROVIDER_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 transitions the provider into the `READY` state and then emits `PROVIDER_CONTEXT_CHANGED`; otherwise it transitions the provider into the `ERROR` state and then emits `PROVIDER_ERROR`. +If the `on context changed` function terminates normally, the provider emits `PROVIDER_CONTEXT_CHANGED`; otherwise it emits `PROVIDER_ERROR`. 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). @@ -205,8 +206,8 @@ see: [static-context paradigm](../glossary.md#static-context-paradigm) > While the provider's `on context changed` function is executing, associated `RECONCILING` handlers **MUST** run. -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. +The provider must emit `PROVIDER_RECONCILING` while it 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. see: [provider event types](../types.md#provider-events), [provider events](#51-provider-events), [provider context reconciliation](02-providers.md#26-provider-context-reconciliation) @@ -214,36 +215,36 @@ see: [provider event types](../types.md#provider-events), [provider events](#51- > 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. -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). +The provider must emit `PROVIDER_CONTEXT_CHANGED` after it has successfully reconciled the context. +The `on context changed` function may be invoked simultaneously or in quick succession; the provider must be prepared to handle this case. + 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, 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). +The provider must emit `PROVIDER_ERROR` if reconciling the context fails. +The `on context changed` function may be invoked simultaneously or in quick succession; the provider must be prepared to handle that. 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 to the status associated with that event **before** the SDK invokes any event handlers for that event, so that handlers observe a consistent status. +> When a provider emits an event, the SDK **MUST** update the `provider status` to the status associated with that event **before** invoking any event handlers for that event, so that handlers observe a consistent status. -Some providers may emit events spontaneously, based on changes in their internal state (connections, caches, etc). -The SDK must update its internal representation of the provider's state accordingly: +The SDK derives provider status entirely from events emitted by the provider. +The table below summarizes the association between events and provider status: -| Event | Associated Status | -| -------------------------------- | ------------------------------------------------------- | -| `PROVIDER_READY` | `READY` | -| `PROVIDER_STALE` | `STALE` | -| `PROVIDER_ERROR` | `ERROR`/`FATAL`* | -| `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) | +| Event | Associated Status | +| -------------------------------- | ------------------------------------------- | +| `PROVIDER_READY` | `READY` | +| `PROVIDER_STALE` | `STALE` | +| `PROVIDER_ERROR` | `ERROR`/`FATAL`* | +| `PROVIDER_CONFIGURATION_CHANGED` | N/A (provider remains in its current state) | +| `PROVIDER_CONTEXT_CHANGED` | `READY` | +| `PROVIDER_RECONCILING` | `RECONCILING` | \* If the `error code` associated with the error indicates `PROVIDER_FATAL`, the state is set to `FATAL` -see: [provider lifecycle management](01-flag-evaluation.md#17-provider-lifecycle-management), [provider status](../types.md#provider-status) [error codes](../types.md#error-code) +see: [provider lifecycle management](01-flag-evaluation.md#17-provider-lifecycle-management), [provider status](../types.md#provider-status), [error codes](../types.md#error-code) diff --git a/tools/specification_parser/lint_json_output.py b/tools/specification_parser/lint_json_output.py index 2073ee359..c3099fb1a 100644 --- a/tools/specification_parser/lint_json_output.py +++ b/tools/specification_parser/lint_json_output.py @@ -23,7 +23,7 @@ def main(f): try: for entry in entries: if entry.get('RFC 2119 keyword') is None and \ - 'condition' not in entry['id'].lower(): + 'requirement' in entry['id'].lower(): print(f"{jsonfile.name}: Rule {entry['id']} is missing a RFC 2119 keyword", file=sys.stderr) errors += 1 pass