open-feature / open-feature/flagd

Provider specification: is a flagd provider re-initializable after shutdown?

Open
#2,048 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Go
Stars
997
Forks
136
Avg merge
4d 8h
Merged PRs (30d)
11

Description

The provider specification's state diagram shows shutdown returning a provider from READY, STALE or ERROR to NOT_READY — the state initialization runs from. What it does not say, in prose, is whether initializing again from there is supported.

Implementers have had to infer it, and inferred differently.

SDK resolver re-initialize after shutdown behaviour
Python in-process works
JavaScript in-process works
Go in-process fails second Init() waits for a sync that never completes, and times out
Go RPC fails Shutdown() reverts the provider's own flag so Init() proceeds, but the event stream never signals ready again and Init() returns provider initialization deadline exceeded
Python RPC fails shutdown() closes the gRPC channel; the second initialize() does not rebuild it, and evaluation raises ValueError: Cannot invoke RPC on closed channel!
Java both refused FlagdProviderSyncResources never clears isInitialized, and initialize() refuses while isShutDown is set, so the second call returns immediately having rebuilt nothing
JavaScript RPC not determinable the scenario fails, but with Flag not found — a pre-existing race in flagd-testbed's /start, not a reuse failure. Unmeasurable until that is fixed.

Two of seven configurations do what the diagram implies. The rest fail in four distinct ways, and two resolvers of the same provider disagree with each other in both Python and Go.

Every row above is measured by running a shutdown/re-initialize cycle. That matters more than it sounds, because source inspection gets this wrong: Go's provider layer looks reusable — Shutdown() explicitly reverts its initialised flag, which is more than the OpenFeature specification asks — yet the refusal is a layer down in the transport. Conversely JavaScript's in-process resolver looks like it declines — disconnect() closes the sync client and nothing visibly reconstructs it — and it works. If you are checking your own implementation, run it.

Why the specification is the right place to settle it

The OpenFeature specification deliberately leaves this open: Requirement 2.5.2 says a provider SHOULD revert to its uninitialized state, and its supporting text says "some providers may allow reinitialization from this state". So none of the above is non-conformant, and this is not a bug report.

But flagd's providers are implementations of one product, and the provider specification exists so they agree. An application that shuts a flagd provider down and initializes it again — between test cases, on reconfiguration, in a reconnect path — works in two configurations and does not in four, with nothing to warn it. Two resolvers of the same provider disagree with each other, in both Python and Go. That is what the document is for.

The ask

Say whether re-initialization from NOT_READY is supported. The diagram implies yes; the prose is silent; implementations split roughly evenly.

  • If yes — each failing transport needs rebuilding on a second initialize(): a channel, an event stream, a sync.
  • If no — say so, because the diagram currently implies the opposite, and Java should still revert to NOT_READY rather than recording a terminal state.

One point holds either way: three of the four failures are indistinguishable, to a caller, from a broken backend. Go returns provider initialization deadline exceeded, its in-process resolver times out, and Python RPC raises on a closed channel. None of them says "reuse is not supported". If the answer is "no", refusing explicitly — as Java does — is the better behaviour, and Java is the one implementation that already gets that part right.

How it surfaced

The cross-language provider conformance suite (open-feature/spec#417) has a scenario "A provider that was shut down can be initialized again". It was originally untagged and therefore mandatory, on the reading that 2.5.2's "revert to its uninitialized state" is observable as exactly one thing. That inference does not hold, and we got it wrong first — the Java result was briefly recorded as a flagd defect and was nearly filed here as one. It is now gated behind a @reinitialization capability, so declining reuse reads as a declared-capability skip rather than a failure. Worth stating, because the same misreading is easy to repeat.

If flagd settles this, flagd-testbed's provider harness is where it stops drifting again: every SDK's flagd e2e suite runs it, so one scenario there holds all four implementations to the answer. Happy to open that PR once there is an answer to encode.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the linked flagd provider specification and OpenFeature Requirement 2.5.2, then compare them with the shutdown/re-initialize results in this issue. Decide whether re-initialization from NOT_READY is supported; done means the prose and state diagram agree, with the flagd-testbed conformance scenario able to encode that decision.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, grpc, java, javascript, python
Domain
documentation
Issue type
Documentation
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.