Skip to content

feat: support isolated API instances#1928

Open
marcozabel wants to merge 3 commits intoopen-feature:mainfrom
marcozabel:feat/isolated-api-instances
Open

feat: support isolated API instances#1928
marcozabel wants to merge 3 commits intoopen-feature:mainfrom
marcozabel:feat/isolated-api-instances

Conversation

@marcozabel
Copy link
Copy Markdown

@marcozabel marcozabel commented Apr 16, 2026

This PR

This PR introduces support for creating isolated OpenFeature API instances, each with their own providers, hooks, context, and event handling - enabling multi-tenant or side-by-side usage without shared global state.

Related Issues

Fixes #1925

Notes

Follow-up Tasks

How to test

@marcozabel marcozabel requested review from a team as code owners April 16, 2026 15:00
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for isolated OpenFeature API instances by adding a createIsolated method to OpenFeatureAPI and a new OpenFeatureAPIFactory in a dedicated package. These changes allow for independent state management (providers, hooks, context) across different instances, satisfying specification requirements. Feedback includes a necessary fix for a compilation error in the tests due to unhandled exceptions, a recommendation to move the shared process-wide lock to an instance level for better performance isolation, and a concern regarding the completeness of global state restoration in the test cleanup logic.

Comment thread src/test/java/dev/openfeature/sdk/isolated/IsolatedAPITest.java Outdated
Comment thread src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java Outdated
Comment thread src/test/java/dev/openfeature/sdk/isolated/IsolatedAPITest.java Outdated
@marcozabel marcozabel force-pushed the feat/isolated-api-instances branch 5 times, most recently from 518c67f to dcf7a52 Compare April 16, 2026 15:11
Signed-off-by: marcozabel <marco.zabel@dynatrace.com>
@marcozabel marcozabel force-pushed the feat/isolated-api-instances branch from dcf7a52 to e7b91f3 Compare April 16, 2026 15:15
Signed-off-by: marcozabel <marco.zabel@dynatrace.com>
Signed-off-by: marcozabel <marco.zabel@dynatrace.com>
@sonarqubecloud
Copy link
Copy Markdown

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 16, 2026

Codecov Report

❌ Patch coverage is 88.88889% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 93.22%. Comparing base (c69bb0e) to head (3815faa).

Files with missing lines Patch % Lines
...c/main/java/dev/openfeature/sdk/EventProvider.java 80.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #1928      +/-   ##
============================================
+ Coverage     92.25%   93.22%   +0.96%     
- Complexity      653      658       +5     
============================================
  Files            59       60       +1     
  Lines          1589     1595       +6     
  Branches        179      179              
============================================
+ Hits           1466     1487      +21     
+ Misses           76       62      -14     
+ Partials         47       46       -1     
Flag Coverage Δ
unittests 93.22% <88.88%> (+0.96%) ⬆️

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

☔ View full report in Codecov by Sentry.
📢 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.

* @return a new API instance
* @see OpenFeatureAPI#createIsolated()
*/
public static OpenFeatureAPI createAPI() {
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.

I don't fully see the point of this method/class. OpenFeatureAPI.createIsolated() has the exact same visibility level and does the same thing. I think this will only lead to confusion

// relies on a ready event to be emitted
emitterExecutor.submit(() -> {
try (var ignored = OpenFeatureAPI.lock.readLockAutoCloseable()) {
try (var ignored = localLock != null ? localLock.readLockAutoCloseable() : null) {
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.

Why is the lock optional? IIRC, we needed that lock to avoid race conditions when emitting events

// package-private multi-read/single-write lock
static AutoCloseableReentrantReadWriteLock lock = new AutoCloseableReentrantReadWriteLock();
// package-private multi-read/single-write lock (instance-level for isolation)
AutoCloseableReentrantReadWriteLock lock = new AutoCloseableReentrantReadWriteLock();
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.

Can this lock be final?


apiLock = setupLock(apiLock, mockInnerReadLock(), mockInnerWriteLock());
OpenFeatureAPI.lock = apiLock;
api.lock = apiLock;
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.

Instead of setting the lock like this, can we also add a package private constructor to the api that takes a lock?

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.

Support isolated API instances

2 participants