Skip to content

Proposal to move Provider Status field from provider to SDK, refine semantics around ContextChange events #238

@toddbaert

Description

@toddbaert

Background

Currently, provider authors are required to maintain a field indicating their state: (NOT_READY/READY/STALE/ERROR).
This burdens provider authors in a few ways that are not particularly ergonomic, and creates potential foot-guns.
This is particularly concerning for SDKs which rely heavily on events (React, for example); it's quite possible for the "suspense" functionality to break and suspend indefinitely if a provider fails to correctly set their READY state after they are initialized. Additionally, we may need additional states/events to support emerging client use-cases.

Problem 1: State-management at Initialization

The SDK itself calls initialize() on any provider in state NOT_READY.
The SDK then emits an READY or ERROR event based on the successful/unsuccessful termination of initialize().
However, it's the provider's responsibility to then set the state correctly.
This results in confusion, and frequently provider authors neglect to properly set the providers status (this is often caught in PRs).

Problem 2: State-management at context change (client paradigm only)

The static context paradigm (client-side paradigm) defines a context change method that can be implemented by the provider, which performs whatever operations are needed to reconcile the evaluated flags with the new context.
Similar to problem above, the burden is put on the provider author to correctly update state (as well as emit the STALE event) when this function is called: https://openfeature.dev/specification/sections/events/#condition-534. Additionally, the semantics of PROVIDER_STALE event may not be ideal or sufficient for this use case; STALE was not originally intended to indicate the providers flags needed to be resolved due to a context change.

Solution:

To resolve the both the above, the provider state should be maintained by the SDK, and become only notional to the application author, and the SDK should emit all events that are part of the provider lifecycle.
This will make the provider lifecycle entirely the domain of the SDK; the provider would need only to implement the optional initialize and shutdown, and context change (client-only) methods, and the SDK should handle the rest.
From a provider-author's perspective, providers would become stateless; the authors job is simply to implement the desired interfaces, while the SDK would manage the entire lifecycle as appropriate, based on the implemented methods.
SDKs would run initialize/onContextChange/shutdown if they are defined, and maintain any state if necessary.
Providers would only have to ensure initialize and shutdown are idempotent and re-entrant.
For the onContextChange, the SDK would also emit a new event (PROVIDER_CONTEXT_CHANGE_PENDING?) when the context is updated as well.
See the example below:

Current example of a compliant onContextChange:

  async onContextChange(oldContext: EvaluationContext, newContext: EvaluationContext): Promise<void> {
    this.status = ProviderStatus.STALE; // author must manually emit STALE
    this.events.emit(ProviderEvents.Stale); // author must manually set state
    await someAsyncWorkToUpdateFlags();
    this.status = ProviderStatus.READY; // semantics here are not well-defined
  }

Proposed simplification:

  // SDK will have already emit a PROVIDER_CONTEXT_CHANGE_PENDING event
  async onContextChange(oldContext: EvaluationContext, newContext: EvaluationContext): Promise<void> {
    await someAsyncWorkToUpdateFlags();
    // when this method returns/resolves, SDK emits PROVIDER_CONTEXT_CHANGED
  }

Considerations

  • Most implementations can probably do this in a non-breaking way by simply deprecating their public ProviderStatus enum, and ignoring the public provider state accessor of any provider
  • Spec point 5.3.3 should likely be removed since we are removing the notion of provider state.
    • The recent additions of a method to await/block during the initialization of the provider have reduced the importance of this point, it's now recommended to await the provider when setting it, which means than a successful initialization and errors with initialization can be easily identified without using event-handlers for such things.
  • Providers which maintain a persistent connection may emit ERROR/READY events when they disconnect/recover; these may still be "manually" emitted from providers since they originate with state changes specific to the provider implementation, and not lifecycle changes originating from the SDK.
  • ⚠️ As pointed out here, we may want to consider a new event instead of PROVIDER_STALE, to indicate the specific circumstance that a provider is pending an update after a context change.

Next steps

I've done a PoC in JS with this, which you can see here.

If we agree on this path forward, we can remove this requirement from the spec, which would entail the update and removal of a few points. Follow-up implementation in SDKs should be relatively simple and non-breaking.

@lukas-reining @kinyoklion @thomaspoignant @beeme1mr @liran2000 @luizgribeiro @jonathannorris @fabriziodemaria @vahidlazio if this sounds compelling I will open a spec PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions