-
Notifications
You must be signed in to change notification settings - Fork 473
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
InvalidOperationException thrown from CreateRequestContainer cause performance issue #2832
Comments
@fannydengdeng some initial questions:
One possibility, but it's just a theory, is that the request pipeline is being re-entered. Since this is still OData 7.x, it is using the legacy A possible workaround around would be to clear the OData feature. If you have retries or fallbacks, that can be done at the start of the pipeline. If it's from re-entering route matching, it may have to be done elsewhere in the pipeline. I'm not 100% sure where. Clearing the feature is simple enough. app.Use((context, next) => {
context.Features.Set(default(IODataFeature));
return next(context);
}); @xuzhg I did some digging and I can't find anywhere that API Versioning calls |
@commonsensesoftware Thanks for quickly digging into. Very appreciate it. |
Thank you for the quick investigation!
We are using API Versioning 4.1 and MVC for routing.
Our services are on .NET 6 and have not migrated from OData 7.x to 8.x yet. Hence the need to stay on 4.1. Yes from debugging I was seeing the route matching being re-entered for a single request (not a retry), though I don't know why. The service that was taking the performance hit has put in a workaround to sort the EdmModels by most frequently used ApiVersion during route registration so it alleviates the pressure for now. We have plans to move to use endpoint routing once dotnet/aspnet-api-versioning#1012 is sorted out. Or go directly to OData 8.x. |
The common thing that can cause re-entry into the routing system is a variance of route constraints in the route templates between API versions. For example, if you had Are you somehow connected to dotnet/aspnet-api-versioning#1012? If not, you shouldn't be waiting. I'm waiting on a repro before I even consider whether any work will be done. All versions < If you're targeting .NET 6, then you should be using Asp.Versioning.OData If you have more questions or comments on dotnet/aspnet-api-versioning#1012, please add them there. If you have more questions or need help in your transition, feel free to start a discussion or open an issue in the API Versioning repo. |
Interestingly,
Question: is it expected that route template is "{*odataPath}" for all the routes? This is how we set up the routing
As for the transition, yes I'm connected to dotnet/aspnet-api-versioning#1012. We have not been able to prioritize upgrading to OData 8.x hence we can't yet use Asp.Versioning.OData 6.4.0 which depends on OData 8.x. |
Yes. OData < 8.0 uses its own matching process and routing conventions. ASP.NET Core sees this as the catch-all route template If you're able to post the simplest repro for dotnet/aspnet-api-versioning#1012 or for this issue, I'm happy to investigate further. I've been unable to repro the scenario myself. |
Thank you @commonsensesoftware. Here is a simple repro for this issue https://github.com/fannydengdeng/ODataApiVersioning/tree/main/ODataApiVersioning |
After digging into this more, I understand what is happening. Apologies for not reading the OP more carefully. The issue was outlined pretty clearly. @xuzhg The issue is related to a First-Chance Exception. No exception is actually observed, but as @fannydengdeng points out, I understand why internal static IServiceProvider GetRequestContainer(this HttpRequest request, string routeName) =>
request.ODataFeature().RequestContainer ?? request.CreateRequestContainer(routeName); This would address the issue without adding anything new to the public surface area (e.g. it can ship in a patch). Ultimately, this issue is only indirectly related to API Versioning. OData should be handling this scenario more gracefully. API Versioning does not observe or handle the exception (that I've found). This issue does not exist in OData 8.0+. It's your call to service the change and in which versions. |
As brought up by #1616, CreateRequestContainer throws an InvalidOperationException when it's called multiple times. In the case of multiple routes being matched by the RouteConstraintMatcher for multiple ApiVersions, the exception here gets thrown if a higher version is requested, and it eventually gets swallowed. Even though it doesn't cause the request to fail, the problem is we have found it has caused an increased in lock contention and CPU usage in one of our high traffic services due to excess cost of the exceptions being thrown.
Assemblies affected
Microsoft.AspNetCore.OData 7.5.17
Reproduce steps
Expected result
No InvalidOperationException is thrown at all.
Actual result
InvalidOperationException is thrown then swallowed.
Additional detail
Here are some stack details:
Exception stack
Lock contention from walking the stack, which could be due to the high rate of exceptions.
The text was updated successfully, but these errors were encountered: