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.