OData / odata.net

ODataLib: Open Data Protocol - .NET Libraries and Frameworks
https://docs.microsoft.com/odata
Other
683 stars 349 forks source link

High CPU Odata Usage AddWithResize() #3015

Closed haledv closed 1 month ago

haledv commented 1 month ago

We are using Odata to query data, and we noticed some high CPU usages when the Api gets hit by some medium traffic.

Assemblies affected

Microsoft.OData.Client" Version="7.20.0" Microsoft.OData.Extensions.Client" Version="1.0.6"

Actual result

CPU Spikes

Additional detail

I am not sure if we are using the Client somehow wrong or something special, but tracing the application revelead that the cpu is mostly burned here:

image

If someone has any tips on what to look for when the stack trace looks like that.

habbes commented 1 month ago

@haledv Thanks for sharing the profiler screenshot. Does this spike occur throughout, over multiple requests, or just during the first request? Do you create a new DataServiceContext per request? The LoadServiceModelFromNetwork call that appears in the screenshot usually occurs when the DataServiceContext is trying to load the IEdmModel and no custom loader has been defined.

If you generated your client using OData Connected Service or OData CLI, this should not occur, since they also generate a customer loader. Could you share more details or code snippets of how your DataServiceContext is constructed and configured?

If you created a custom DataServiceContext manually (without using our code generation tools), then you should configure configure the DataServiceContext.Format with a customer service model loader as suggested in this article: https://learn.microsoft.com/en-us/odata/client/using-poco-classes#loading-the-service-model

Namely, you should override the DataServiceContext.Format.LoadServiceModel property with a custom method or delegate that returns the instance of IEdmModel that represents the service's schema. Ideally this should not perform repeated network call or parsing on each call. The model should be loaded and parsed once the cached in some static field for later retrieval.

context.LoadServiceModel =() => GetParsedEdmModel();
context.UseJson();

This setup should happen before the first request is made by the DataServiceContext.

You can parse an IEdmModel from a string or stream using CsdlReader: https://learn.microsoft.com/en-us/odata/odatalib/edm/read-write-model#using-the-csdlreader-apis

haledv commented 1 month ago

Hi, thx for the quick reply.

It occured while multiple requests where sent at once, which came from a user burst on the facing API.

It was created manually without the CLI. Following your advice and the links provided, we cached the Edm Model so no repeated network calls where done and it solved the Problem completely. No more redudant Network Calls and CPU spikes have gone.

Thank for the Advice!

habbes commented 1 month ago

Thanks @haledv for getting back. We should add this to our performance guidelines: https://github.com/MicrosoftDocs/OData-docs/issues/318