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

Singleton implementations are caching ThreadAbortException #731

Open
kaevne opened this issue Jul 10, 2019 · 7 comments

Comments

Projects
None yet
2 participants
@kaevne
Copy link

commented Jul 10, 2019

We're using Simple Injector at extremely high scale (6 digits in servers, 300 billion requests/day). On a couple servers a day, we are seeing issues where IIS is issuing a ThreadAbort in the middle of Singleton instantiation. This hurts one request, but Simple Injector's use of Lazy<T> caches the ThreadAbortException **for all subsequent calls.

The only way to recover the process is to restart it.

Can we have a fix to change the Lazy<T> usage in Simple Injector from ExecutionAndPublication to PublicationOnly so that Exceptioning instantiations are retried instead of cached?

DispatchException=SimpleInjector.ActivationException: Thread was being aborted.
---> System.Threading.ThreadAbortException: Thread was being aborted.
at System.SZArrayHelper.GetEnumerator[T]()
at System.Linq.Enumerable.<ConcatIterator>d__59`1.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at SimpleInjector.Decorators.DecoratorExpressionInterceptor.CreateOverriddenParameters(Type serviceType  ConstructorInfo decoratorConstructor  Expression decorateeExpression  InstanceProducer realProducer  ServiceTypeDecoratorInfo info)
at SimpleInjector.Decorators.DecoratorExpressionInterceptor.CreateRegistration(Type serviceType  ConstructorInfo decoratorConstructor  Expression decorateeExpression  InstanceProducer realProducer  ServiceTypeDecoratorInfo info)
at SimpleInjector.Decorators.ServiceDecoratorExpressionInterceptor.CreateRegistrationForDecorator()
at SimpleInjector.Decorators.ServiceDecoratorExpressionInterceptor.ApplyDecorator(Type closedDecoratorType)
at SimpleInjector.Decorators.DecoratorInterceptor.TryToApplyDecorator(ExpressionBuiltEventArgs e)
at SimpleInjector.Decorators.DecoratorInterceptor.ExpressionBuilt(Object sender  ExpressionBuiltEventArgs e)
at System.EventHandler`1.Invoke(Object sender  TEventArgs e)
at SimpleInjector.Container.OnExpressionBuilt(ExpressionBuiltEventArgs e  InstanceProducer instanceProducer)
at SimpleInjector.InstanceProducer.BuildExpressionInternal()
at System.Lazy`1.CreateValue()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Lazy`1.get_Value()
at SimpleInjector.InstanceProducer.BuildInstanceCreator()
at SimpleInjector.InstanceProducer.BuildAndReplaceInstanceCreatorAndCreateFirstInstance()
at SimpleInjector.InstanceProducer.GetInstance()
--- End of inner exception stack trace ---
at SimpleInjector.InstanceProducer.GetInstance()
at SimpleInjector.Internals.ContainerControlledCollection`1.<GetEnumerator>d__28.MoveNext()
at Microsoft.Exchange.OData.DependencyInjection.CompositeEventHandler`1.Handle(TEvent eventDetails)
at Microsoft.Exchange.Services.OData.Web.RequestBroker.Dispatch(AsyncCallback asyncCallback);ServiceDiagnostics_ReportException=SimpleInjector.ActivationException: Thread was being aborted. ---> System.Threading.ThreadAbortException: Thread was being aborted.
at System.SZArrayHelper.GetEnumerator[T]()
at System.Linq.Enumerable.<ConcatIterator>d__59`1.MoveNext()
at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at SimpleInjector.Decorators.DecoratorExpressionInterceptor.CreateOverriddenParameters(Type serviceType  ConstructorInfo decoratorConstructor  Expression decorateeExpression  InstanceProducer realProducer  ServiceTypeDecoratorInfo info)
at SimpleInjector.Decorators.DecoratorExpressionInterceptor.CreateRegistration(Type serviceType  ConstructorInfo decoratorConstructor  Expression decorateeExpression  InstanceProducer realProducer  ServiceTypeDecoratorInfo info)
at SimpleInjector.Decorators.ServiceDecoratorExpressionInterceptor.CreateRegistrationForDecorator()
at SimpleInjector.Decorators.ServiceDecoratorExpressionInterceptor.ApplyDecorator(Type closedDecoratorType)
at SimpleInjector.Decorators.DecoratorInterceptor.TryToApplyDecorator(ExpressionBuiltEventArgs e)
at SimpleInjector.Decorators.DecoratorInterceptor.ExpressionBuilt(Object sender  ExpressionBuiltEventArgs e)
at System.EventHandler`1.Invoke(Object sender  TEventArgs e)
at SimpleInjector.Container.OnExpressionBuilt(ExpressionBuiltEventArgs e  InstanceProducer instanceProducer)
at SimpleInjector.InstanceProducer.BuildExpressionInternal()
at System.Lazy`1.CreateValue()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Lazy`1.get_Value()
at SimpleInjector.InstanceProducer.BuildInstanceCreator()
at SimpleInjector.InstanceProducer.BuildAndReplaceInstanceCreatorAndCreateFirstInstance()
at SimpleInjector.InstanceProducer.GetInstance()
--- End of inner exception stack trace ---
at SimpleInjector.InstanceProducer.GetInstance()
at SimpleInjector.Internals.ContainerControlledCollection`1.<GetEnumerator>d__28.MoveNext()
at Microsoft.Exchange.OData.DependencyInjection.CompositeEventHandler`1.Handle(TEvent eventDetails)
at Microsoft.Exchange.Services.OData.Web.RequestBroker.Dispatch(AsyncCallback asyncCallback);
@dotnetjunkie

This comment has been minimized.

Copy link
Collaborator

commented Jul 10, 2019

Can we have a fix to change the Lazy implementation from ExecutionAndPublication to PublicationOnly so that Exceptioning instantiations are retried instead of cached?

Although that would indeed fix your problem, it would introduce a different problem. Such change would break the guarantee of there ever be a single instance created. So this is, unfortunately, not an option.

I'm afraid that the only way to fix this, is by reimplementing Lazy<T> from scratch and prevent caching in cache the delegate fails.

@kaevne

This comment has been minimized.

Copy link
Author

commented Jul 10, 2019

Yes, this is how we dealt with the problem too. We created our own custom Lazy implementation that does not cache Exceptions due to the insidious problems that it creates at scale.

I suppose our only other option is to catch these particular exceptions, and discard the SimpleInjector container.

@dotnetjunkie

This comment has been minimized.

Copy link
Collaborator

commented Jul 10, 2019

I will try to create a patch release that fixes the problem.

In the meantime, a workaround you can use is calling .Verify() on startup. Calling Verify ensures all singletons are created. This prevents any runtime exception from corrupting Lazy<T>'s state. As an additional advantage, it pre-compiles the complete container. Although it slows down start-up, it speeds up every first resolve for a component.

@kaevne

This comment has been minimized.

Copy link
Author

commented Jul 10, 2019

Ahh ok, that is a good workaround. We aren't calling verify currently at startup, only in unit tests. Thank you Steven!

@dotnetjunkie

This comment has been minimized.

Copy link
Collaborator

commented Jul 10, 2019

I will keep you updated on progress, but do note that this might take some time for me to fix as this runs deep through the codebase.

@kaevne

This comment has been minimized.

Copy link
Author

commented Jul 11, 2019

Thank you! This is the only issue we have with SimpleInjector at the moment. It performs at high scale with minimal latency impact.

@dotnetjunkie dotnetjunkie added this to the v4.6.1 milestone Jul 15, 2019

@dotnetjunkie

This comment has been minimized.

Copy link
Collaborator

commented Jul 15, 2019

bug-731 branch created.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.