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

ExternalRegistrySource refactoring impacted on activation stack depth and causing MaxDepthExceeded #1180

Closed
Alexandr-Gurinovich opened this issue Jul 28, 2020 · 2 comments

Comments

@Alexandr-Gurinovich
Copy link

Describe the Bug

After #960 (which was caused by #946 ) activation stack depth increased in case resolving types registered in root scope from grandchild scope. In #960 reuse of ExternalComponentRegistration was removed so child, grandchild, great-grandchild ... scopes will have own ExternalComponentRegistration instances for parent scopes registrations. Now
Activation stack depth = Type dependency depth * scope hierarchy depth,
which causes DependencyResolutionException with MaxDepthExceeded earlier than before.

Steps to Reproduce

Consider we have a type A registered in root scope. Type A has 16-depth dependency. If we try to resolve type A from a grandchild scope well get next exception.

Autofac.Core.DependencyResolutionException: An exception was thrown while activating
λ:B16
-> λ:B16
-> B16
-> λ:B15
-> λ:B15
-> λ:B15
-> λ:B14
-> λ:B14
-> B14
-> λ:B13
-> λ:B13
-> B13
-> λ:B12
-> λ:B12
-> B12
-> λ:B11
-> λ:B11
-> λ:B11
-> λ:B10
-> λ:B10
-> B10
-> λ:B9
-> λ:B9
-> B9
-> λ:B8
-> λ:B8
-> B8
-> λ:B7
-> λ:B7
-> B7
-> λ:B6
-> λ:B6
-> B6
-> λ:B5
-> λ:B5
-> B5
-> λ:B4
-> λ:B4
-> B4
-> λ:B3
-> λ:B3
-> B3
-> λ:B2
-> λ:B2
-> λ:B2
-> λ:B1
-> λ:B1
-> B1
-> λ:A
-> λ:A.
---> Autofac.Core.DependencyResolutionException: Probable circular dependency between factory-scoped components. Chain includes 'Activator = A (ReflectionActivator), Services = [A], Lifetime = Autofac.Core.Lifetime.CurrentScopeLifetime, Sharing = None, Ownership = OwnedByLifetimeScope'
   at Autofac.Core.Resolving.CircularDependencyDetector.CheckForCircularDependency(IComponentRegistration registration, Stack`1 activationStack, Int32 callDepth)

Each dependent type repeats 3 times. If we tried to resolve type A from great-grandchild we would see that every dependent type repeats 4 times and so on.

Sample test with deep scopes hierarchy:

public class ReproTest
{
        private class A
        {
        }

        private class B
        {
            public B(A a) { }
        }

        private class C
        {
            public C(B b) { }
        }

        private class D
        {
            public D(C c) { }
        }

        private class E
        {
            public E(D d) { }
        }

        private class F
        {
            public F(E e) { }
        }

        private class G
        {
            public G(F f) { }
        }

        private class H
        {
            public H(G g) { }
        }

        private class I
        {
            public I(H h) { }
        }

        private class J
        {
            public J(I i) { }
        }

        private class K
        {
            public K(J j) { }
        }

        [Fact]
        public void Resolve_ElevenDepthDependentTypeFromGreatGreatGrandchilsScope_ResolutionIsDone()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<A>();
            builder.RegisterType<B>();
            builder.RegisterType<C>();
            builder.RegisterType<D>();
            builder.RegisterType<E>();
            builder.RegisterType<F>();
            builder.RegisterType<G>();
            builder.RegisterType<H>();
            builder.RegisterType<I>();
            builder.RegisterType<J>();
            builder.RegisterType<K>();
            var root = builder.Build(ContainerBuildOptions.IgnoreStartableComponents);
            var child = root.BeginLifetimeScope("child", b => b.RegisterType<object>());
            var grandchild = child.BeginLifetimeScope("grandchild", b => b.RegisterType<object>());
            var greatGrandchild = grandchild.BeginLifetimeScope("greatGrandchild", b => b.RegisterType<object>());
            var greatGreatGrandchild = greatGrandchild.BeginLifetimeScope("greatGreatGrandchild", b => b.RegisterType<object>());

            var k = greatGreatGrandchild.Resolve<K>();
            Assert.NotNull(k);
        }
}

Expected Behavior

Activation stack depth not to depend on scopes hierarchy depth so much.

Dependency Versions

Faced the issue on 4.9.1

@tillig
Copy link
Member

tillig commented Jul 28, 2020

Short term, the Autofac.Core.Resolving.CircularDependencyDetector.MaxResolveDepth field is private, but static and intentionally so you can use reflection to tweak it. Increase that as needed. It is also intentionally not exposed in the public API because we reserve the right to change it, but that should get you over the hump.

If you're using Autofac 4.9.1 as indicated, you should upgrade. Autofac 5 was released six months ago and we're on 5.2.0 right now.

Longer term, we're working on a version 6.0.0 release with a huge internals overhaul. Circular dependency detection has been improved and, where possible, we use the actual remaining stack depth before stack overflow rather than having a hard limit.

I'm going to close this issue for now since we have, to the best of our abilities, addressed it both short and long term. If you continue having the issue after upgrading to v6 when it's released, we can reopen and revisit it.

@tillig tillig closed this as completed Jul 28, 2020
@Alexandr-Gurinovich
Copy link
Author

@tillig thanks for the prompt reply

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

No branches or pull requests

2 participants