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

Fix concurrent request handling for OpenAPI documents #57972

Merged
merged 5 commits into from
Sep 22, 2024

Conversation

xC0dex
Copy link
Contributor

@xC0dex xC0dex commented Sep 19, 2024

Fix: Concurrent request for OpenApi documents

  • You've read the Contributor Guide and Code of Conduct.
  • You've included unit or integration tests for your change, where applicable.
  • You've included inline docs for your change, where applicable.
  • There's an open issue for the PR that you are making. If you'd like to propose a new feature or change, please open an issue to discuss the change or find an existing issue.

Description

This PR allows concurrent requests for OpenApi documents by switching from a Dictionary to a ConcurrentDictionary. The fields _schemas, SchemasByReference and _operationTransformerContextCache had to be changed to ConcurrentDictionary to pass the test. Additionally, I updated _referenceIdCounter for consistency.

Fixes #57876

@xC0dex xC0dex requested review from captainsafia and a team as code owners September 19, 2024 19:39
@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label Used by the dotnet-issue-labeler to label those issues which couldn't be triaged automatically label Sep 19, 2024
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Sep 19, 2024
@xC0dex
Copy link
Contributor Author

xC0dex commented Sep 19, 2024

@dotnet-policy-service agree

};

// Act
var results = await Task.WhenAll(requests);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if using Parallel with a bigger number of operations would make this more likely to always run requests that race against each other?

Copy link
Contributor Author

@xC0dex xC0dex Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. That was an excellent idea. I switched to Parallel.ForAsync and it turned out that the _operationTransformerContextCache field must also be a ConcurrentDictionary.

@martincostello martincostello added feature-openapi area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc and removed needs-area-label Used by the dotnet-issue-labeler to label those issues which couldn't be triaged automatically labels Sep 20, 2024
@captainsafia captainsafia changed the title Fix: Concurrent request for OpenApi documents Fix concurrent request handling for OpenAPI documents Sep 20, 2024
@captainsafia
Copy link
Member

/azp run

Copy link

Azure Pipelines successfully started running 3 pipeline(s).

Copy link
Member

@captainsafia captainsafia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🚀

var targetSchema = valueFactory(key);
_schemas.Add(key, targetSchema);
return targetSchema;
return _schemas.GetOrAdd(key, valueFactory(key));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@BrennanConroy shared some feedback here that we can use a different overload of the GetOrAdd method here to avoid another possible race condition:

Suggested change
return _schemas.GetOrAdd(key, valueFactory(key));
return _schemas.GetOrAdd(key, valueFactory, key);

With this pattern, the GetOrAdd method will do a check for the schema key in the dictionary before calling into the factory.

The current overload used will always call the valueFactory even if the key already exists in the dictionary.

In this case, the valueFactory were calling is an invocation into the JsonSchemaMapper. I dunno if it is particularly prone to race issues but it still doesn't hurt to avoid unnecessarily calling into it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the feedback. One small PR and I already learned a lot!
Unfortunately, the suggested overload does not work because it expects a valueFactory of type Func<TKey, TArg, TValue>. Luckily, there is another overload, so I could do the following:

- return _schemas.GetOrAdd(key, valueFactory(key));
+ return _schemas.GetOrAdd(key, _ => valueFactory(key));

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should work, and will avoid needing to allocate a closure:

return _schemas.GetOrAdd(key, valueFactory);

@captainsafia captainsafia merged commit cd7eec7 into dotnet:main Sep 22, 2024
26 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the 10.0-preview1 milestone Sep 22, 2024
captainsafia pushed a commit that referenced this pull request Sep 23, 2024
* fix: Allow concurrent requests

* test: Update test

* test: Use Parallel.ForEachAsync

* feat: Use valueFactory overload

* feat: Pass valueFactory directly
wtgodbe pushed a commit that referenced this pull request Sep 25, 2024
* Fix JsonUnmappedMemberHandling attribute handling to close #57981

* Fix enum handling for MVC actions to close #57979

* Fix self-referential schema handling to close #58006

* Fix concurrent request handling for OpenAPI documents (#57972)

* fix: Allow concurrent requests

* test: Update test

* test: Use Parallel.ForEachAsync

* feat: Use valueFactory overload

* feat: Pass valueFactory directly

* Harden self-referencing schema ID check

---------

Co-authored-by: Justin Lampe <xC0dex@users.noreply.github.com>
captainsafia added a commit that referenced this pull request Sep 26, 2024
* Fix JsonUnmappedMemberHandling attribute handling to close #57981

* Fix enum handling for MVC actions to close #57979

* Fix self-referential schema handling to close #58006

* Fix concurrent request handling for OpenAPI documents (#57972)

* fix: Allow concurrent requests

* test: Update test

* test: Use Parallel.ForEachAsync

* feat: Use valueFactory overload

* feat: Pass valueFactory directly

* Harden self-referencing schema ID check

---------

Co-authored-by: Justin Lampe <xC0dex@users.noreply.github.com>
captainsafia added a commit that referenced this pull request Sep 26, 2024
… (#58096)

* Fix JsonUnmappedMemberHandling attribute handling to close #57981

* Fix enum handling for MVC actions to close #57979

* Fix self-referential schema handling to close #58006

* Fix concurrent request handling for OpenAPI documents (#57972)

* fix: Allow concurrent requests

* test: Update test

* test: Use Parallel.ForEachAsync

* feat: Use valueFactory overload

* feat: Pass valueFactory directly

* Harden self-referencing schema ID check

---------

Co-authored-by: Justin Lampe <xC0dex@users.noreply.github.com>
@xC0dex xC0dex deleted the fix/openapi-concurrent-requests branch November 8, 2024 20:45
captainsafia pushed a commit that referenced this pull request Dec 31, 2024
* fix: Allow concurrent requests

* test: Update test

* test: Use Parallel.ForEachAsync

* feat: Use valueFactory overload

* feat: Pass valueFactory directly
captainsafia added a commit that referenced this pull request Dec 31, 2024
… (#58096)

* Fix JsonUnmappedMemberHandling attribute handling to close #57981

* Fix enum handling for MVC actions to close #57979

* Fix self-referential schema handling to close #58006

* Fix concurrent request handling for OpenAPI documents (#57972)

* fix: Allow concurrent requests

* test: Update test

* test: Use Parallel.ForEachAsync

* feat: Use valueFactory overload

* feat: Pass valueFactory directly

* Harden self-referencing schema ID check

---------

Co-authored-by: Justin Lampe <xC0dex@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-minimal Includes minimal APIs, endpoint filters, parameter binding, request delegate generator etc community-contribution Indicates that the PR has been added by a community member feature-openapi
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Minimal API Group with OpenAPI and Return Type of Result<TResult> creates incorrect OpenAPI document.
4 participants