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

Closed
kaevne opened this issue Jul 10, 2019 · 9 comments
Closed

Singleton implementations are caching ThreadAbortException #731

kaevne opened this issue Jul 10, 2019 · 9 comments
Milestone

Comments

@kaevne
Copy link

kaevne 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
Copy link
Collaborator

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
Copy link
Author

kaevne 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
Copy link
Collaborator

dotnetjunkie 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
Copy link
Author

kaevne 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
Copy link
Collaborator

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
Copy link
Author

kaevne 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
Copy link
Collaborator

bug-731 branch created.

@dotnetjunkie dotnetjunkie modified the milestones: v4.6.1, v4.6.2 Jul 23, 2019
@dotnetjunkie
Copy link
Collaborator

I pushed v4.6.2 to NuGet that fixes this issue.

@kaevne
Copy link
Author

kaevne commented Jul 29, 2019

Thank you! We'll upgrade as soon as possible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants