Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: Add support for provider shutdown and status. #158

Merged
merged 2 commits into from
Nov 14, 2023

Conversation

kinyoklion
Copy link
Member

This PR

Adds support for provider initialization, shutdown, and status.

This PR does not add support for events, but does put several items in place to facilitate their implementation.

Related Issues

Partially Addresses #126

Notes

Follow-up Tasks

How to test

@kinyoklion kinyoklion changed the title ffeat: Add support for provider shutdown and status. feat: Add support for provider shutdown and status. Oct 31, 2023
Copy link

codecov bot commented Oct 31, 2023

Codecov Report

Attention: 4 lines in your changes are missing coverage. Please review.

Comparison is base (9c42d4a) 93.91% compared to head (8108e63) 94.30%.
Report is 7 commits behind head on main.

Files Patch % Lines
src/OpenFeature/FeatureProvider.cs 57.14% 3 Missing ⚠️
src/OpenFeature/ProviderRepository.cs 99.15% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #158      +/-   ##
==========================================
+ Coverage   93.91%   94.30%   +0.39%     
==========================================
  Files          20       21       +1     
  Lines         542      650     +108     
  Branches       39       54      +15     
==========================================
+ Hits          509      613     +104     
- Misses         20       23       +3     
- Partials       13       14       +1     

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

Signed-off-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
/// <param name="featureProvider">Implementation of <see cref="FeatureProvider"/></param>
public void SetProvider(FeatureProvider featureProvider)
public async Task SetProvider(FeatureProvider featureProvider)
Copy link
Member Author

Choose a reason for hiding this comment

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

So, returning a task here is a breaking API change. This seems like the most appropriate action. Await the method if you care that it finishes initialization, don't await if you do not.

Other options would be adding another method, but this method would just be async without a way to know it finished, versus being sync.

This all applies to named providers as well.

Copy link
Member

Choose a reason for hiding this comment

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

Yea I've been thinking about this myself its quite common to offer bother sync and async libraries especially when working on a old legacy project in full framework. Lets just leave it for now and see if anyone raise concern before going ahead an introduce a sync based methods.

Copy link
Member

Choose a reason for hiding this comment

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

So, returning a task here is a breaking API change. This seems like the most appropriate action. Await the method if you care that it finishes initialization, don't await if you do not.

Other options would be adding another method, but this method would just be async without a way to know it finished, versus being sync.

"events" are the mechanism by which you can call this synchronously and "know" the outcome (your READY/ERROR event handlers will run). I know they don't exist on this SDK yet, but they will eventually.

Other options would be adding another method

This is what's been done in Java and JS, and what the spec recommends: https://openfeature.dev/specification/sections/flag-evaluation/#requirement-118

Copy link
Member Author

@kinyoklion kinyoklion Nov 1, 2023

Choose a reason for hiding this comment

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

So, the spec makes a lot of sense for Java. Complete-able futures are not super convenient, and you would probably need something like kotlin on top to make a useful API out of it.

Dotnet though has reasonably proper async, and the general means would be a method you either await, or don't await, or synchronously wait.

The language in the spec doesn't really work, and I am not sure what would.

Usually if you have a sync and async method you would have.

doTheThing and doTheThingAsync. But, doTheThing would be synchronous, not async without completion.

The spec recommends setProviderAndWait, which implies sync, but I don't think we actually want to call .Wait on behalf of the user. They can make the choice to asynchronously await, not await or wait, or to synchronously wait, and we don't have to worry about quirks in their thread execution model that could lead to things like a deadlock.

Either approach will result in an API that is not intuitive for the ecosystem. It can be done, it is just unfortunate.

Copy link
Member Author

Choose a reason for hiding this comment

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

(To elaborate AndWait or Wait in general have been used to provide sync APIs where the base API is async, so it would be a confusing name)

Copy link
Member Author

Choose a reason for hiding this comment

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

I think, if the API is fully async, then we don't need to worry about the Async suffix. It really is a case of libraries that implemented things synchronously, and then needed async methods as well.

The interface for this API is almost all Async, and it happened that there was this sync method, so making it async would provide a consistent async interface.

It may be worth checking the rest of the API to see if other items should move to async with this.

Copy link
Member Author

Choose a reason for hiding this comment

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

(I guess the client is really where things are async, and the "Api" doesn't have that many methods that are async, or would conceivably be async.)

Copy link
Member

Choose a reason for hiding this comment

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

I'm fine with keeping it all async and not providing a sync api. I think since we aren't 1.x.x yet its the best time to make a breaking change to SetProvider, i think we just need to call it out in the release notes.

Copy link
Member

Choose a reason for hiding this comment

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

This SDK is already version 1.3.1 but we can release a version 2.0 if it makes sense. I think it does make sense in this case since it simplifies waiting for a provider to initialize and the migration process should be straightforward.

Copy link
Member

Choose a reason for hiding this comment

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

I'm fine with the breaking change, especially if we agree we'll do the same breaking change in JS, which has the same async/await semantics.

{
this._featureProviderLock.ExitWriteLock();
}
await this._repository.SetProvider(featureProvider, this.GetContext()).ConfigureAwait(false);
Copy link
Member Author

Choose a reason for hiding this comment

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

I refactored the collection management out of the Api and moved it into the ProviderRepository. This is similar to the Java SDK.

@@ -20,6 +21,74 @@ public void OpenFeature_Should_Be_Singleton()
openFeature.Should().BeSameAs(openFeature2);
}

[Fact]
Copy link
Member Author

Choose a reason for hiding this comment

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

I added some basic tests here, but the tests for the registry are much more comprehensive.

@kinyoklion kinyoklion marked this pull request as ready for review October 31, 2023 20:18
@kinyoklion kinyoklion requested a review from a team as a code owner October 31, 2023 20:18
public async Task SetProvider(
FeatureProvider featureProvider,
EvaluationContext context,
Action<FeatureProvider> afterSet = null,
Copy link
Member Author

Choose a reason for hiding this comment

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

All these "actions" can be used to facilitate implementing events support. I didn't want to put too much stuff together, so this should be a nice middle ground.

@beeme1mr
Copy link
Member

Sorry for the delayed response. Now that we have the ability to perform initialization from within the provider, it makes sense to make this an async call. Unfortunately, that means it will be a breaking change, but it should be easy for users to absorb this change. I did bring this up during the last community call, and no one had any concerns. I think I'll also do something similar in the JS SDK.

Before we release a 2.0 version, could the rest of the SDK be audited to make sure there are no other breaking changes we should consider?

FYI @toddbaert @kinyoklion @benjiro

@beeme1mr beeme1mr changed the title feat: Add support for provider shutdown and status. feat!: Add support for provider shutdown and status. Nov 13, 2023
@beeme1mr beeme1mr added the breaking-change Changes in exposed interfaces or behaviour label Nov 13, 2023
this._providersLock.ExitWriteLock();
}

await InitProvider(featureProvider, context, afterInitialization, afterError).ConfigureAwait(false);
Copy link
Member

Choose a reason for hiding this comment

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

At first I thought it was necessary to put this in the locked block, but I don't think it matters, since the important thing is to maintain thread safety of the provider dict, not make any guarantees about provider initialization order.

Copy link
Member

@toddbaert toddbaert left a comment

Choose a reason for hiding this comment

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

Testing looks good. I'm OK with the breaking change as discussed, but in that case I think we should probably wait for events and release both of these changes together.

Co-authored-by: Todd Baert <todd.baert@dynatrace.com>
Signed-off-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
@kinyoklion
Copy link
Member Author

Testing looks good. I'm OK with the breaking change as discussed, but in that case I think we should probably wait for events and release both of these changes together.

Should I re-target this PR to a feature branch?

@benjiro
Copy link
Member

benjiro commented Nov 14, 2023

Testing looks good. I'm OK with the breaking change as discussed, but in that case I think we should probably wait for events and release both of these changes together.

Should I re-target this PR to a feature branch?

I would think it's fine to merge we just don't release/publish the next version until events are done.

@toddbaert
Copy link
Member

Testing looks good. I'm OK with the breaking change as discussed, but in that case I think we should probably wait for events and release both of these changes together.

Should I re-target this PR to a feature branch?

No no, we'll just refrain from releasing if possible until events. If we need to release for some reason, it won't be a huge problem to release this on its own. The worse case would be we have another breaking change for events (not likely, it didn't happen in other SDKs) and we release a v3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking-change Changes in exposed interfaces or behaviour
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants