Autofac resolving singleton creates bottleneck

I am using Autofac in an asp.net MVC application and have encountered a lock problem. Anytime a service relies on singleton, this service is resolved from the root lifecycle area. This is because Autofac:

  • resolves singleton components from the root area
  • resolves any components from the root area that have dependencies that must be resolved from root.

In addition, when resolving any region, Autofac blocks this region. I think these are great design decisions. My problem is with the wrong classes, which depend on singletones and with slow constructors. This creates a bottleneck for anyone who needs the permission of a single singlet.

Since this is an MVC application, each request is mapped to some MVC that is introduced by the constructor. In addition, most of my controllers accept dependencies on various singleton services (logging, caching, etc.).

For things with fast constructors, this is not a problem. But as soon as one poorly written class is requested, my throughput tanks, because every new request is blocked on this incorrect constructor. For instance:

given these 3 classes

//registered as SingleInstance() class MySingleton {} class BadTransient { public BadTransient(MySingleton s) { Thread.Sleep(5000); } } class FriendlyTransient {} 

allowed as

 using(var scope = container.BeginLifetimeScope("nested scope")) { //resolves from child scope var myFriend = scope.Resolve<FriendlyTransient>(); //resolves from root because it a singleton var singleton = scope.Resolve<MySingleton>(); //resolves from root because it has a singleton dependency. //**** this locks the root scope for 5 seconds //**** no one else can resolve singletons. var troublemaker = scope.Resolve<BadTransient>(); } 

Is there any way to avoid this bottleneck?

The obvious answer is to have fast constructors. The reality is that not all constructors in my code base can be guaranteed quickly. There is a lot of outdated code, there is a lot of code that relies on third-party code, there the code looks fast, but depends on the code that is not there, there is code that is usually fast, but breaks in strange circumstances, etc. Developer training only works to some extent.

We fixed the constructors as they were found, but I need a more active solution. My users are not allowed to run my QA.

Note. I don't care about slow constructors that aren't singlet-specific. They will block their lifetime, but will not block other threads.

+6
source share
2 answers

I fixed the problem by re-registering all singletones using the autoplay close syntax.
This preserves the build logic in autofac, but eliminates the loneliness of the child’s life area. Essentially:

 builder.Register<MySingleton>().AsSelf().AsImplementedInterfaces(); //.. other registrations var container = builder.Build(); // now resolve each singleton, forcing all to be constructed if not already // and then register the instance var builder2 = new ContainerBuilder(); var mySingleton = container.Resolve<MySingleton>(); builder2.Register(c => mySingleton).AsSelf().AsImplementedInterfaces(); builder2.Update(container); //..... var scope = container.BeginLifetimeScope("child scope"); scope.Resolve<MySingleton>(); //not resolved from root! 

Then, since there are many singletones, and I can request their types programmatically, I wrote a function that takes a list of types and runs the above code. It should do a little magic of reflection, although it only works when you start the application at the end of the usual autofac registration code.

 void ReRegisterSingletons(IContainer container, List<Type> singletonTypes) { var builder= new ContainerBuilder(); foreach(var type in singletonTypes) { var resolved = container.Resolve(type); var method = this.GetType().GetMethod("ReRegister").MakeGenericMethod(new []{type}); method.Invoke(this, new []{resolved}); } builder.Update(container); } void Register<T>(ContainerBuilder builder, object singleton) { var theObj = (T)singleton; //a typed lambda was the only way I could get both the class name and the interface names to resolve from the child scope. RegisterInstance still resolves from root, and the non-generic lamba register can resolve the class name from child scope but not the interface names... builder.Register(c => theObj).AsSelf().AsImplementedInterfaces(); } 
+2
source

I agree with @nemesv that building an object should be quick, but that also means no initialization in OnActivated . Rather, you should do it lazily when the component is first used. For example, by implementing a proxy server or some Lazy<T> internally.

But if you have an application with fairly high bandwidth and concurrency characteristics, and you have verified through performance profiling that locking is a bottleneck, you might consider switching to an insecure IoC container.

+2
source

All Articles