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
Circular Dependency Changes #1148
Conversation
Define a new SegmentedStack<T> that allows us maintain segments of the request stack, meaning we can resolve decorators without allocating an entirely new operation.
…e passed our typicaly max resolve depth, and only throw once it fails.
This was definitely a hole in my life, to be sure. Looks like some good improvements, though, so... I guess I'll dig in. 😆 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love the smell of a PR in the morning. Smells like... victory.
Couple minor questions. Otherwise, 🤠 YAHOOOOOOO
src/Autofac/Core/Resolving/Pipeline/ResolveRequestContextBase.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🥇
I know how much @tillig likes looking at a PR every day, so.....
I've made two changes to the way circular dependencies are detected.
Max Depth
The first is to the 'max depth' constraint, which currently has a limit of 50. Based on @VonOgre's suggestion of
RuntimeHelpers.EnsureSufficientExecutionStack()
in #575 (👍), there is another method,RuntimeHelpers.TryEnsureSufficientExecutionStack()
. On netstandard2.1, when we hit the normal 50 limit, rather than stop immediately, we keep going untilTryEnsureSufficientExecutionStack
returns false.These methods do not exist prior to netstandard2.1, so the existing '50' limit is applied.
This should please anyone with insanely deep service graphs; on my testing, I hit 891 resolve requests before it stopped.
Request Stack Change - Depth and Segments
We talked already about pre-allocating the request stack in #1127 ; that has been done (default size of 16). That's actually pretty deep for 90% of users I'd guess; it'll then double each resize after that.
In addition, I wanted to fix an annoying problem with decorators, where they had to reach out and execute a top-level
ResolveOperation
instead of just resolving the decorator in the sameIComponentContext
resulting in a whole other allocation of theResolveOperation
, and generally some sad code. They did this because they needed a fresh circular dependency detection stack.To counter this, I've replaced the use of
Stack<T>
with a newSegmentedStack<T>
. This type behaves almost exactly like a regular stack, except it allows you define a point in the stack to start a new 'segment'. The enumerator for the stack then only operates within the segment.In this way we can mark a point in the request stack beyond which we do not look for any further dependencies.
In the
DecoratorMiddleware
:This gives us some memory and performance gains on resolving decorators (KeylessGenericBenchmark):
v6
request-stack