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

refactor: Use ValueTask on plumbing methods #185

Closed
wants to merge 2 commits into from

Conversation

austindrenski
Copy link
Member

Switch to ValueTask on methods that are likely to complete synchronously and/or be populated with contrib-/user-provided delegates.

See: #184


This follows #184 with an idea (probably best punted for post-2.0.0) to switch plumbing-type methods from Task to ValueTask to take advantage of the zero-cost-sync-completion sugar that the dotnet/runtime folks have generously given us to play with.

I've got a bigger PR planned where I'll propose returning ValueTask<T> on the IFeatureClient methods in order to hide a TTL caching layer that users can use to amortize provider requests (e.g. users could configure IFeatureClient to cache a value for flag K for N seconds, and for those N seconds any subsequent requests to any IFeatureClient for flag K would result in a sync read from a memory cache).

But that's for later, and for now this PR just looks to make those user-/contrib-provided plumbing methods super lightweight on the scalding hotpath.

@austindrenski austindrenski force-pushed the value-task branch 2 times, most recently from adaf67d to e7d66d0 Compare January 17, 2024 01:11
Copy link

codecov bot commented Jan 17, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (1a14f6c) 0.00% compared to head (0c7fabf) 94.11%.

Additional details and impacted files
@@            Coverage Diff            @@
##           main     #185       +/-   ##
=========================================
+ Coverage      0   94.11%   +94.11%     
=========================================
  Files         0       23       +23     
  Lines         0      951      +951     
  Branches      0      101      +101     
=========================================
+ Hits          0      895      +895     
- Misses        0       32       +32     
- Partials      0       24       +24     

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

Signed-off-by: Austin Drenski <austin@austindrenski.io>
Switch to ValueTask on methods that are likely to complete
synchronously and/or be populated with contrib-/user-provided
delegates.

Signed-off-by: Austin Drenski <austin@austindrenski.io>
@toddbaert
Copy link
Member

I've started updating this and #184

@toddbaert
Copy link
Member

Closing and continuing with #268

@toddbaert toddbaert closed this May 1, 2024
toddbaert added a commit that referenced this pull request Jun 17, 2024
This PR is a combination of
#184 and
#185. Changes include:

- adding cancellation tokens
- in all cases where async operations include side-effects
(`setProviderAsync`, `InitializeAsync`, I've specified in the in-line
doc that the cancellation token's purpose is to cancel such side-effects
- so setting a provider and canceling that operation still results in
that provider's being set, but async side-effect should be cancelled.
I'm interested in feedback here, I think we need to consider the
semantics around this... I suppose the alternative would be to always
ensure any state changes only occur after async side-effects, if they
weren't cancelled beforehand.
- adding "Async" suffix to all async methods
- remove deprecated sync `SetProvider` methods 
- Using `ValueTask` for hook methods
- I've decided against converting all `Tasks` to `ValueTasks`, from the
[official .NET
docs](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask?view=net-8.0):
> the default choice for any asynchronous method that does not return a
result should be to return a
[Task](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task?view=net-8.0).
Only if performance analysis proves it worthwhile should a ValueTask be
used instead of a
[Task](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task?view=net-8.0).
- I think for hooks, `ValueTask` especially makes sense since often
hooks are synchronous, in fact async hooks are probably the less likely
variant.
- I've kept the resolver methods as `Task`, but there could be an
argument for making them `ValueTask`, since some providers resolve
asynchronously.
- I'm still a bit dubious on the entire idea of `ValueTask`, so I'm
really interested in feedback here
- associated test updates

UPDATE:

After chewing on this for a night, I'm starting to feel:
- We should simply remove cancellation tokens from Init/Shutdown. We can
always add them later, which would be non-breaking. I think the value is
low and the complexity is potentially high.
- ValueTask is only a good idea for hooks, because:
  - Hooks will very often be synchronous under the hood
- We (SDK authors) await the hooks, not consumer code, so we can be
careful of the potential pitfalls of ValueTask. I think everywhere else
we should stick to Task.

---------

Signed-off-by: Austin Drenski <austin@austindrenski.io>
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Austin Drenski <austin@austindrenski.io>
Co-authored-by: André Silva <2493377+askpt@users.noreply.github.com>
Co-authored-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
arttonoyan pushed a commit to arttonoyan/dotnet-sdk that referenced this pull request Nov 17, 2024
This PR is a combination of
open-feature#184 and
open-feature#185. Changes include:

- adding cancellation tokens
- in all cases where async operations include side-effects
(`setProviderAsync`, `InitializeAsync`, I've specified in the in-line
doc that the cancellation token's purpose is to cancel such side-effects
- so setting a provider and canceling that operation still results in
that provider's being set, but async side-effect should be cancelled.
I'm interested in feedback here, I think we need to consider the
semantics around this... I suppose the alternative would be to always
ensure any state changes only occur after async side-effects, if they
weren't cancelled beforehand.
- adding "Async" suffix to all async methods
- remove deprecated sync `SetProvider` methods
- Using `ValueTask` for hook methods
- I've decided against converting all `Tasks` to `ValueTasks`, from the
[official .NET
docs](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.valuetask?view=net-8.0):
> the default choice for any asynchronous method that does not return a
result should be to return a
[Task](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task?view=net-8.0).
Only if performance analysis proves it worthwhile should a ValueTask be
used instead of a
[Task](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task?view=net-8.0).
- I think for hooks, `ValueTask` especially makes sense since often
hooks are synchronous, in fact async hooks are probably the less likely
variant.
- I've kept the resolver methods as `Task`, but there could be an
argument for making them `ValueTask`, since some providers resolve
asynchronously.
- I'm still a bit dubious on the entire idea of `ValueTask`, so I'm
really interested in feedback here
- associated test updates

UPDATE:

After chewing on this for a night, I'm starting to feel:
- We should simply remove cancellation tokens from Init/Shutdown. We can
always add them later, which would be non-breaking. I think the value is
low and the complexity is potentially high.
- ValueTask is only a good idea for hooks, because:
  - Hooks will very often be synchronous under the hood
- We (SDK authors) await the hooks, not consumer code, so we can be
careful of the potential pitfalls of ValueTask. I think everywhere else
we should stick to Task.

---------

Signed-off-by: Austin Drenski <austin@austindrenski.io>
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
Co-authored-by: Austin Drenski <austin@austindrenski.io>
Co-authored-by: André Silva <2493377+askpt@users.noreply.github.com>
Co-authored-by: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com>
Signed-off-by: Artyom Tonoyan <artonoyan@servicetitan.com>
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.

2 participants