Skip to content

fix: disallow provider registration and initialization after API shutdown - #523

Open
erka wants to merge 3 commits into
mainfrom
rd/evaluation-api-state
Open

fix: disallow provider registration and initialization after API shutdown#523
erka wants to merge 3 commits into
mainfrom
rd/evaluation-api-state

Conversation

@erka

@erka erka commented Jul 28, 2026

Copy link
Copy Markdown
Member

This PR

  • track evaluationAPIState (active/shutdown)
  • guard SetProvider and SetDomainProvider to reject calls after shutdown
  • wait for in-flight provider initializations before shutting down process

closes #495

@erka
erka requested review from a team as code owners July 28, 2026 19:48
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 44157299-5a75-496f-9ba4-79bb761dfcab

📥 Commits

Reviewing files that changed from the base of the PR and between 282169a and ae1aad9.

📒 Files selected for processing (3)
  • openfeature/isolated_api_test.go
  • openfeature/openfeature_api.go
  • openfeature/openfeature_api_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • openfeature/openfeature_api.go
  • openfeature/openfeature_api_test.go
  • openfeature/isolated_api_test.go

📝 Walkthrough

Walkthrough

The change adds lifecycle state gating to EvaluationAPI. Provider initialization is tracked during shutdown. New tests cover cancellation, post-shutdown provider rejection, and provider shutdown ordering.

Changes

Provider lifecycle coordination

Layer / File(s) Summary
Shutdown state and provider registration gates
openfeature/openfeature_api.go, openfeature/isolated_api_test.go
EvaluationAPI tracks active and shutdown states. Provider registration returns errAPIShutdown after shutdown begins, including domain-scoped registration. Canceled shutdown restores the active state.
Initialization and teardown coordination
openfeature/openfeature_api.go, openfeature/openfeature_api_test.go
Provider initialization is tracked with a wait group. Shutdown waits for initialization before provider teardown. Tests verify provider lifecycle ordering.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant EvaluationAPI
  participant initNew
  participant Provider
  EvaluationAPI->>initNew: start tracked initialization
  EvaluationAPI->>EvaluationAPI: transition to shutdown
  EvaluationAPI->>initNew: wait for initialization completion
  initNew->>Provider: initialize
  EvaluationAPI->>Provider: call Shutdown after initialization settles
Loading

Possibly related PRs

Suggested reviewers: sahidvelji

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the lifecycle changes that prevent provider registration and initialization after API shutdown.
Description check ✅ Passed The description accurately summarizes API state tracking, post-shutdown rejection, and synchronization of provider initialization.
Linked Issues check ✅ Passed The changes address issue #495 by gating provider lifecycle operations and waiting for in-flight initialization before shutdown.
Out of Scope Changes check ✅ Passed The implementation and added tests remain within the linked issue scope of correcting provider initialization and shutdown ordering.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.74%. Comparing base (0fb6f31) to head (ae1aad9).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #523      +/-   ##
==========================================
+ Coverage   86.55%   86.74%   +0.19%     
==========================================
  Files          22       22              
  Lines        2119     2135      +16     
==========================================
+ Hits         1834     1852      +18     
+ Misses        241      240       -1     
+ Partials       44       43       -1     
Flag Coverage Δ
e2e 86.74% <100.00%> (+0.19%) ⬆️
unit 86.74% <100.00%> (+0.19%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openfeature/openfeature_api_test.go`:
- Around line 95-128: Make TestProviderInitShutdown coordinate the provider
lifecycle with initStarted and releaseInit channels: have ttprovider.Init signal
initStarted and block until releaseInit, start SetProvider asynchronously, wait
for initStarted before calling Shutdown, assert Shutdown does not return while
Init is blocked, then release Init and verify both operations complete with
Shutdown invoked only after initialization finishes.

In `@openfeature/openfeature_api.go`:
- Around line 282-286: Update the initialization path around the state check and
initializerWithContext call so admission is atomic with shutdown: prevent
Shutdown from transitioning state while the active-state validation and Init
invocation are in progress, using the existing read-lock or equivalent admission
protocol. Ensure no initializerWithContext execution can begin after shutdown
starts, while preserving the existing shutdown error behavior.
- Around line 421-425: The Shutdown path around initWg.Wait must not restore
evaluationAPIStateActive while initialization is still in flight. Replace the
detachable Wait goroutine with a mutex-protected in-flight counter and
completion channel, and have Shutdown select between cancellation and that
completion signal; only restore active after initWg.Wait has returned,
preventing later provider registration from calling initWg.Add during an
outstanding Wait.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 0b67a8b5-702e-4c9d-b53e-0cba8c05c1c9

📥 Commits

Reviewing files that changed from the base of the PR and between e75753b and efa5f22.

📒 Files selected for processing (3)
  • openfeature/isolated_api_test.go
  • openfeature/openfeature_api.go
  • openfeature/openfeature_api_test.go

Comment thread openfeature/openfeature_api_test.go
Comment thread openfeature/openfeature_api.go Outdated
Comment thread openfeature/openfeature_api.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openfeature/openfeature_api.go (1)

408-424: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Do not permanently mark the API shut down when initialization waiting is canceled.

On ctx.Done(), Shutdown returns before provider teardown, but leaves evaluationAPIStateShutdown set. Every later Shutdown then returns nil immediately, so in-flight providers may never receive Shutdown and bindings/event resources remain active. Keep a distinct “shutting down” state and let a later call resume or join the pending teardown; only treat the API as fully shut down after cleanup completes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openfeature/openfeature_api.go` around lines 408 - 424, The Shutdown flow
around evaluationAPIStateShutdown must distinguish shutdown-in-progress from
fully completed shutdown. Do not set the terminal shutdown state before waiting
on a cancelable context; on ctx.Done(), preserve pending teardown so a later
Shutdown call can resume or join it, and transition to
evaluationAPIStateShutdown only after provider teardown and binding/event
cleanup complete. Ensure repeated calls do not skip cleanup while initialization
remains in flight.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@openfeature/openfeature_api.go`:
- Around line 408-424: The Shutdown flow around evaluationAPIStateShutdown must
distinguish shutdown-in-progress from fully completed shutdown. Do not set the
terminal shutdown state before waiting on a cancelable context; on ctx.Done(),
preserve pending teardown so a later Shutdown call can resume or join it, and
transition to evaluationAPIStateShutdown only after provider teardown and
binding/event cleanup complete. Ensure repeated calls do not skip cleanup while
initialization remains in flight.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d6fc3114-c042-42d6-a2f5-6981069239cf

📥 Commits

Reviewing files that changed from the base of the PR and between efa5f22 and 1c106e7.

📒 Files selected for processing (1)
  • openfeature/openfeature_api.go

@erka
erka marked this pull request as draft July 28, 2026 21:55
@erka
erka force-pushed the rd/evaluation-api-state branch 2 times, most recently from 170aa86 to fb7c5f4 Compare July 29, 2026 10:31
@erka
erka marked this pull request as ready for review July 29, 2026 10:34
@erka
erka requested a review from sahidvelji July 29, 2026 10:35

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
openfeature/isolated_api_test.go (1)

400-410: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Make the in-flight initialization test deterministic.

The fixed 100ms delay does not prove Shutdown entered while initialization was still running; if scheduling lets initialization finish first, the test can pass even if the wait coordination is removed. Use explicit start/release synchronization and assert that Shutdown remains blocked until initialization is released.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openfeature/isolated_api_test.go` around lines 400 - 410, Make the test
around SetProvider and Shutdown deterministic by replacing the fixed initDelay
timing with explicit synchronization channels or equivalent start/release
signals in testContextAwareProvider. Ensure the test observes that asynchronous
initialization has started, invokes Shutdown with an already-cancelled context,
and asserts Shutdown remains blocked until the initialization release signal is
sent; then release initialization and verify Shutdown completes with the
expected active state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@openfeature/isolated_api_test.go`:
- Around line 414-421: Remove the conditional “state after shutdown” t.Errorf
from the err assertion block in the shutdown test; retain the standalone
instance.state assertion, which is the authoritative state check.

---

Nitpick comments:
In `@openfeature/isolated_api_test.go`:
- Around line 400-410: Make the test around SetProvider and Shutdown
deterministic by replacing the fixed initDelay timing with explicit
synchronization channels or equivalent start/release signals in
testContextAwareProvider. Ensure the test observes that asynchronous
initialization has started, invokes Shutdown with an already-cancelled context,
and asserts Shutdown remains blocked until the initialization release signal is
sent; then release initialization and verify Shutdown completes with the
expected active state.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ffc53c99-6f63-4d24-bbd4-8b129da924ef

📥 Commits

Reviewing files that changed from the base of the PR and between 1c106e7 and fb7c5f4.

📒 Files selected for processing (2)
  • openfeature/isolated_api_test.go
  • openfeature/openfeature_api.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • openfeature/openfeature_api.go

Comment thread openfeature/isolated_api_test.go
erka added 3 commits July 31, 2026 10:50
…down

- track evaluationAPIState (active/shutdown)
- guard SetProvider and SetDomainProvider to reject calls after shutdown
- wait for in-flight provider initializations before shutting down process

Signed-off-by: Roman Dmytrenko <rdmytrenko@gmail.com>
Signed-off-by: Roman Dmytrenko <rdmytrenko@gmail.com>
Signed-off-by: Roman Dmytrenko <rdmytrenko@gmail.com>
@erka
erka force-pushed the rd/evaluation-api-state branch from 282169a to ae1aad9 Compare July 31, 2026 09:51

@sahidvelji sahidvelji left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review pass. The lifecycle gating and the Shutdown() teardown ordering fix look correct, and the added tests are race-clean. Two inline comments, plus one outside-diff note.

These are distinct from CodeRabbit's earlier concurrency comments: those were filed against the earlier implementation that used atomic state.Load()/state.Store() and a detached Wait goroutine. In the current code, state and all initWg operations are accessed under the pre-existing a.mu write lock (state checks in setProvider/setDomainProvider and the transitions + initWg.Wait() in Shutdown all run inside a.mu.Lock() sections; initWg.Add(1) in initNew is only reached from those locked paths). That serialization closes the observe-then-transition and Add-during-Wait races CodeRabbit described, so those comments no longer apply.

Outside-diff note — the provider-replacement path still has issue #495's ordering bug (openfeature_api.go:310-319, shutdownOld). The fix covers the API Shutdown() teardown path via initWg.Wait(), but replacing a provider before its Init finishes is unaffected: SetProvider(A) then SetProvider(B) triggers shutdownOld(ctx, A), which spawns a goroutine calling A.Shutdown()/ShutdownWithContext immediately, with no coordination with A's still-in-flight Init goroutine. So A can receive Shutdown before or during its Init — the exact class of invalid ordering #495 describes. Worth confirming whether #495 should be considered fully closed, or whether the same await-in-flight-init coordination should extend to shutdownOld.

Comment thread openfeature/openfeature_api.go
Comment thread openfeature/openfeature_api.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Provider lifecycle calls may occur in invalid order

2 participants