Azure / azure-functions-host

The host/runtime that powers Azure Functions
https://functions.azure.com
MIT License
1.94k stars 442 forks source link

Scoped registrations resolve same instance between multiple httprequests #7605

Open Schuer84 opened 3 years ago

Schuer84 commented 3 years ago

We registered our DbContext as scoped, but we noticed, if we start multiple http requests, the context gets shared between the different requests, which leads to threading issues:

{ type: 'NotSupportedException', message: "A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe." }

Any feedback on how to fix this would be greatly appreciated.

Our setup:

soninaren commented 3 years ago

@Schuer84 is there a way for you to share a simplified repro. That would help us understand the problem and guide you better.

Schuer84 commented 3 years ago

I'll see what I can setup this evening, it might take some time to setup properly. Some background info:

We noticed that the scoped registrations weren't being scoped, as in, a dbcontext was getting created twice per request. This caused troubles for some commandhandlers that were having multiple dependencies which also depended on the context. So we enabled the flag "AzureWebJobsFeatureFlags": "EnableEnhancedScopes". After that the context was getting resolved only once, so the commands were getting properly handled. But then... we noticed the mentioned behaviour above.. multiple requests were hitting the same context while executing queries. Even worse, we also implemented a UserIdentityAccessor that depends on IHttpContextAccessor, to read the http headers. This one is also registered as scoped. And guess what.. people are getting access to data they don't have access to since the instance get shared between multiple requests.

We could eventually do some workarounds, to get these issues fixed.. but not being able to trust on 'scoped' registrations, makes me think azure functions isn't quite production ready yet.

Schuer84 commented 3 years ago

I've managed to reproduce the issue here The problem is, with the enanhced flag enabled, I registered a Singleton which has a dependency on the IServiceProvider. (see the registration of IMessageProvider in the startup) What I find weird, is that I have two classes that require a IServiceProvider There seem to be 2 instances of a service provider. that resolve different instances, which results in the following: (the guids define the instance of the IUserIdentityAccessor)

with the enhanced flag, with singleton registration:

OK - response for request 15
 validation: Header for request 15-3e3e8e84-327a-403f-bdf9-c7bf98c90c6a
 message1: Header for request 23-637f6883-d4d7-41bc-934e-8bdb095435bc
 message2: Hello - Header for request 23-637f6883-d4d7-41bc-934e-8bdb095435bc
 request header: Header for request 15

without the enhanced flag, with singleton registration:

OK - response for request 70
 validation: Header for request 70-e605964d-036f-4504-8bf3-1f2c96581767
 message1: Header for request 70-e605964d-036f-4504-8bf3-1f2c96581767
 message2: Hello - Header for request 70-e605964d-036f-4504-8bf3-1f2c96581767
 request header: Header for request 70

with enhanced flag, but scoped registration:

OK - response for request 98
 validation: Header for request 98-a54edd48-1ccd-4ef6-8265-26725cd02d35
 message1: Header for request 98-a54edd48-1ccd-4ef6-8265-26725cd02d35
 message2: Hello - Header for request 98-a54edd48-1ccd-4ef6-8265-26725cd02d35
 request header: Header for request 98

without enhance, with scoped registration:

OK - response for request 68
 validation: Header for request 68-c19cd76e-7eb8-4b44-a161-bfe1366e3c69
 message1: Header for request 68-c19cd76e-7eb8-4b44-a161-bfe1366e3c69
 message2: Hello - Header for request 68-c19cd76e-7eb8-4b44-a161-bfe1366e3c69
 request header: Header for request 68

In this repro case the fix is to use the scoped registration. I'm not (yet) able to reproduce the fact that some instances are being resolved twice. When I moved the registrations from singleton to scoped in the project the issue was occurring. (without enhanced feature flag) It still resolved the db context twice. So I'm a bit lost why that is happening.