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

Change IOptionsSnapshot to reuse options instances across scopes #56271

Merged
merged 4 commits into from
Aug 6, 2021

Conversation

NinoFloris
Copy link
Contributor

Closes #53793

I decided to keep the cache as a ConcurrentDictionary to head off problematic compatibility issues from switching out the underlying implementation. I'm not clear on the semantics of a DI scope regardless, maybe it's ok to use one in a concurrent fashion?

For the cache I picked capacity 5 for the 99% case, apparently 31 is the default capacity of a ConcurrentDictionary and what was specified in OptionsCache. Capacity 31 sounds fairly wasteful to new up every request but on the flip side you'd get a tiny chance of resizes I guess 🙃

OptionsManager and OptionsCache are effectively dead code now. Something to mark as obsolete in a future version by the api review team?

@ghost ghost added the community-contribution Indicates that the PR has been added by a community member label Jul 24, 2021
@ghost
Copy link

ghost commented Jul 24, 2021

Tagging subscribers to this area: @maryamariyan
See info in area-owners.md if you want to be subscribed.

Issue Details

Closes #53793

I decided to keep the cache as a ConcurrentDictionary to head off problematic compatibility issues from switching out the underlying implementation. I'm not clear on the semantics of a DI scope regardless, maybe it's ok to use one in a concurrent fashion?

For the cache I picked capacity 5 for the 99% case, apparently 31 is the default capacity of a ConcurrentDictionary and what was specified in OptionsCache. Capacity 31 sounds fairly wasteful to new up every request but on the flip side you'd get a tiny chance of resizes I guess 🙃

OptionsManager and OptionsCache are effectively dead code now. Something to mark as obsolete in a future version by the api review team?

Author: NinoFloris
Assignees: -
Labels:

area-Extensions-Options, community-contribution

Milestone: -

@NinoFloris
Copy link
Contributor Author

As this type could be constructed per request I've pushed a commit that has a separate path for unnamed options, skipping allocation of the dictionary entirely if just unnamed options are used.

@NinoFloris NinoFloris force-pushed the fix/ioptionssnapshot branch from 4733049 to 6fd7f10 Compare July 26, 2021 19:09
@NinoFloris
Copy link
Contributor Author

@eerhardt thanks for your review, I've pushed a commit addressing it!

@NinoFloris NinoFloris force-pushed the fix/ioptionssnapshot branch from 58eb65c to 2345495 Compare July 28, 2021 14:08
@NinoFloris
Copy link
Contributor Author

@eerhardt is there anything on my end left before this can be merged?

Copy link
Member

@eerhardt eerhardt left a comment

Choose a reason for hiding this comment

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

I think this looks good.

@maryamariyan @davidfowl - any thoughts before this is merged?

return _unnamedOptionsValue = _optionsMonitor.Get(Options.DefaultName);
}

var cache = _cache ?? Interlocked.CompareExchange(ref _cache, new(concurrencyLevel: 1, capacity: 5, StringComparer.Ordinal), null) ?? _cache;
Copy link
Member

Choose a reason for hiding this comment

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

@stephentoub can you look here? I think it's copied from the other pattern we use in Options<T>

Copy link
Member

Choose a reason for hiding this comment

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

The pattern looks fine. It's saying:

  • Volatile read _cache: if it's not null, use it.
  • It's null, so create a new one and atomically set _cache to a new instance iff _cache is still null.
  • If it wasn't still null, use what it was instead.
  • If it was still null, it's now instead the new instance we set it to, so use that.

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 waiting for your book to come out. Concurrency patterns with @stephentoub

@eerhardt
Copy link
Member

eerhardt commented Aug 6, 2021

runtime-staging failures are unrelated.

@davidfowl
Copy link
Member

I think we're going to have to revert this PR. It breaks scoped services contributing to the value of options snapshots (I now see the tests were changed here to account for those changes).

eerhardt added a commit to eerhardt/runtime that referenced this pull request Aug 17, 2021
eerhardt added a commit that referenced this pull request Aug 17, 2021
…pes (#56271)" (#57570)

* Revert "Change IOptionsSnapshot to reuse options instances across scopes (#56271)"

This reverts commit 8f5f9d0.

* Add a test to ensure ASP.NET's scenario isn't broken again
@ghost ghost locked as resolved and limited conversation to collaborators Sep 15, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Extensions-Options community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

IOptionsSnapshot is very slow
5 participants