Skip to content

Concurrency Issue in OpenApiSchemaStore #58845

@xC0dex

Description

@xC0dex

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

It appears that the current implementation of OpenApiSchemaStore does not handle concurrent requests as expected. While I thought that I resolved the issue in #57972, the problem persists under certain conditions.

Observed Behavior:

When running the service in a Linux-based Docker container, the issue occurs almost every time on the first request.
However, when I test it locally on my Windows machine, I am unable to reproduce this issue. That's why I thought it's fixed.

Expected Behavior

The OpenApiSchemaStore should handle concurrent requests without issues.

Steps To Reproduce

  • Use the Scalar.AspNetCore package.
  • Start the API in a Docker container (In my case Linux-based).
  • Trigger concurrent requests by opening /scalar/v1 in the browser (Scalar initiates two parallel requests on startup, reported here).

Exceptions (if any)

Relevant log output:

fail: Microsoft.AspNetCore.Server.Kestrel[13]
api-1    |       Connection id "0HN805CSINAPJ", Request id "0HN805CSINAPJ:00000003": An unhandled exception was thrown by the application.                                       
api-1    |       System.ArgumentException: An item with the same key has already been added. Key: OpenApiSchemaKey { Type = Scalar.AspNetCore.Playground.Books.Book[], ParameterInfo =  }                                                                                                                                                                         
api-1    |          at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
api-1    |          at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)                                                                                       
api-1    |          at Microsoft.AspNetCore.OpenApi.OpenApiSchemaStore.GetOrAdd(OpenApiSchemaKey key, Func`2 valueFactory)
api-1    |          at Microsoft.AspNetCore.OpenApi.OpenApiSchemaService.GetOrCreateSchemaAsync(Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription parameterDescription, Boolean captureSchemaByRef, CancellationToken cancellationToken)                                                 
api-1    |          at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetResponseAsync(ApiDescription apiDescription, Int32 statusCode, ApiResponseType apiResponseType, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)                                                        
api-1    |          at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetResponsesAsync(ApiDescription description, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)                                                                                                             
api-1    |          at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationAsync(ApiDescription description, HashSet`1 capturedTags, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)
api-1    |          at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOperationsAsync(IGrouping`2 descriptions, HashSet`1 capturedTags, IServiceProvider scopedServiceProvider, IOpenApiOperationTransformer[] operationTransformers, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)                                
api-1    |          at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiPathsAsync(HashSet`1 capturedTags, IServiceProvider scopedServiceProvider, IOpenApiOperationTransformer[] operationTransformers, IOpenApiSchemaTransformer[] schemaTransformers, CancellationToken cancellationToken)                                                        
api-1    |          at Microsoft.AspNetCore.OpenApi.OpenApiDocumentService.GetOpenApiDocumentAsync(IServiceProvider scopedServiceProvider, CancellationToken cancellationToken)
api-1    |          at Microsoft.AspNetCore.Builder.OpenApiEndpointRouteBuilderExtensions.<>c__DisplayClass0_0.<<MapOpenApi>b__0>d.MoveNext()                                    
api-1    |       --- End of stack trace from previous location ---
api-1    |          at Microsoft.AspNetCore.Http.Generated.<GeneratedRouteBuilderExtensions_g>F56B68D2B55B5B7B373BA2E4796D897848BC0F04A969B1AF6260183E8B9E0BAF2__GeneratedRouteBuilderExtensionsCore.<>c__DisplayClass2_0.<<MapGet0>g__RequestHandler|5>d.MoveNext()                                                                                              
api-1    |       --- End of stack trace from previous location ---
api-1    |          at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|7_0(Endpoint endpoint, Task requestTask, ILogger logger)                      
api-1    |          at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
api-1    |          at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)                                                                  
api-1    |          at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)  

.NET Version

9.0.100-rc.2.24474.11

Microsoft.AspNetCore.OpenApi version 9.0.0-rc.2.24474.3

Anything else?

Happy to help with resolving this issue!

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcfeature-openapi

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions