Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autofac resolving a singleton creates a bottleneck

I'm using Autofac in an asp.net MVC app and came across a problem with locking. Anytime a service depends on a singleton, that service is resolved from the root lifetime scope. This is because Autofac:

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

Further, when resolving from any scope, Autofac locks that scope. I think these are fine design decisions. My problem is with misbehaving classes who depend on singletons and have slow constructors. These create a bottleneck for anyone else needing to resolve a singleton.

Since this is in a MVC app, each request is getting mapped to some MVC controller that's constructor injected. Further, most of my controllers take 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 that misbehaving constructor. For example:

given these 3 classes

//registered as SingleInstance()
class MySingleton {}

class BadTransient
{
    public BadTransient(MySingleton s)
    { Thread.Sleep(5000); }
}  

class FriendlyTransient {}

resolved like

using(var scope = container.BeginLifetimeScope("nested scope"))
{

    //resolves from child scope
    var myFriend = scope.Resolve<FriendlyTransient>(); 

    //resolves from root because it's 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 a way I can avoid this bottleneck?

The obvious answer is to have fast constructors. The reality is that not all the constructors in my codebase can be guaranteed to be fast. There's a lot of legacy code, there's lots of code that relies on 3rd party code, there's code that looks fast but depends on code that isn't, there's code that's usually fast but breaks down in odd circumstances, etc. Educating developers only works to a certain extent.

We've been fixing constructors as we find them, but I need a more proactive solution. It's not acceptable to let my users do my QA.

Note: I don't care as much about slow constructors that don't depend on a singleton. They will lock their lifetime scope, but it won't block other threads

like image 434
dan Avatar asked Apr 05 '13 05:04

dan


2 Answers

I agree with @nemesv that object construction should be fast, but this also means not initializing in OnActivated. Rather, you should do this lazily when the component is first used. For instance by implementing a proxy or some Lazy<T> internally.

But if you have an application with rather extreme throughput and concurrency characteristics and you verified through performance profiling that locking is a bottleneck, you could consider switching to a lock-free IoC container.

like image 185
Steven Avatar answered Oct 20 '22 06:10

Steven


I fixed the problem by re-registering all the singletons using autofac's closure syntax.
This keeps construction logic in autofac, but resolves singletons from a child lifetime scope. In essence:

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 singletons and I can query their types programmatically, I wrote a function that takes a list of types and runs the above code. It has to do a little reflection wizardry, though only runs at app startup at the end of the normal 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();
}
like image 25
dan Avatar answered Oct 20 '22 07:10

dan