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

IOptionsMonitor<T>.OnChange is fired whenever anything changes in IConfiguration - pit of failure #109445

Open
petrroll opened this issue Nov 1, 2024 · 2 comments
Labels
area-Extensions-Options enhancement Product code improvement that does NOT require public API changes/additions
Milestone

Comments

@petrroll
Copy link

petrroll commented Nov 1, 2024

IOptionsMonitor<T> is the .NET abstraction to handle dynamically changing configuration. That means changes to config file on filesystem during development but also potentially frequent changes through a dynamic config service s.a. Microsoft's internal ones or others.

It provides an option to register callback IOptionsMonitor<T>.OnChange(...) which is fired when the configuration that's bound to T changes.

One would assume that the callback is only called when the config section specific to the IOptionsMonitor changes. As the docs don't say otherwise.

That is, however, not true (see github issue). The .OnChange callbacks can be invoked even if an unrelated section for some other T changes. Essentially as long as any part of configuration changes, all IOptionsMonitorcan trigger their .OnChange callbacks.

It wouldn't be a problem if everyone handled this correctly and actually did rebuilding only if the config actually changes. But regrettably that's not happening.

I think it's not happening because it's almost impossible to not fall into this pit of failure as nothing in the API suggests it would be necessary. And furthermore, doing the right thing is surprisingly, if not difficult, then cumbersome. OnChange for some reason doesn't surface last version of config, only current; so anyone wanting to properly guard against non-changes needs to save the old version themselves, ... .

It's not a huuuuge problem because usually it's just recreating stuff. In case of polly a stateful policies there might be some impact but genenerally it should be functionally ok. The issue is that if all components start rebuilding with each update, service owners will see spikes that don't make much sense.

What to do with it (some random ideas):

  • Update docs to make sure this caveat is documented. Ideally added as remark even for intellisense on that method.
  • Surface old version of config objects on the callback (separate overload, for backwards compatibility).
  • ?Provide better API for config providers to only raise the event for relevant sub-sections?
  • ?If T is IComparible then only raise the event if something actually changed?
@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Nov 1, 2024
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Nov 1, 2024
@MihaZupan MihaZupan added area-Extensions-Options and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Nov 1, 2024
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-extensions-options
See info in area-owners.md if you want to be subscribed.

@rjgotten
Copy link

rjgotten commented Nov 1, 2024

Provide better API for config providers to only raise the event for relevant sub-sections?

This applies in general to the entirety of Microsoft.Extensions.Configuration and how it manages its data into 'sections' - because the entire approach is flawed at its core.

Sections aren't actually sections. There is no tree of them to speak of.
All they are, are real-time filtered projections into a linear list of fully composed keys.

This also means that things like collection binding, in particular for large pools of items, has to also iterate linearly over EVERY key in the entire config system, up from the root to the leaf, and has to manually perform a StartsWith check on a prefix, followed by an index or dictionary key value.

It can be an incredibly big performance killer, and has in fact been reported before as such in #65885.
Sadly, that issue has been left hanging in the past because any adjustments to make the system more sane and perfomant, would've meant breaking vaunted backward compatibility.

(Meanwhile, the .NET ecosystem did get things like the binding behavior for collections suddenly changing from shallow clones to deep clones dropped down in its lap like it meant nothing. So hopefully everyone will forgive me if I have problems seeing that as little more than a very poor excuse on the maintainers part for trying to dodge out of the responsibility of having to clean up the messes that were made. In any case it speaks of some very poor and disjoint application of policies wrt backwards compatibility.)

@tarekgh tarekgh removed the untriaged New issue has not been triaged by the area owner label Nov 1, 2024
@tarekgh tarekgh added this to the Future milestone Nov 1, 2024
@tarekgh tarekgh added the enhancement Product code improvement that does NOT require public API changes/additions label Nov 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-Extensions-Options enhancement Product code improvement that does NOT require public API changes/additions
Projects
None yet
Development

No branches or pull requests

4 participants