OData / WebApi

OData Web API: A server library built upon ODataLib and WebApi
https://docs.microsoft.com/odata
Other
854 stars 476 forks source link

OData performance extremely bad on AspNetCore 2.0 running on full DotNet Framework 4.7 #1243

Open ovidiubuligan opened 6 years ago

ovidiubuligan commented 6 years ago

I am running WebApi OData on aspnet core with full dotnet framework 4.7

I exposed 2 endpoints on my app . One with OData and one with normal ASPNetCore Web api and manual call to EF. They return about the same data (OData one shoud be more efficent since I select ony 2 columns ). I then ran 2 basic sets of Jmeter Tests on them with 200 concurrent calls to the apis . They the 2 tests look like the folowing: Odata HTTP call : /odata/Customers?$filter=contains(Name,'a') and ClientId eq ${ClientId}&$select=Id,Name REST API HTTP call: /api/values/searchCustomers?custName=a&clientId=${clientId}

I compared performance from OData endpoint to Normal Rest endpoint and the difference was huge . more than 10x difference in time to completion :


Rest API 200 concurrent calls results:
summary =    200 in 00:00:05 =   41.9/s Avg:  1873 Min:   191 Max:  4303 Err:     0 (0.00%)

OData 200 concurrent calls results:
summary =    200 in 00:00:54 =    3.7/s Avg: 30193 Min:  3623 Max: 53782 Err:     0 (0.00%)

Analyzing performance of slow case with Windows Performance Analyzer , it is not related to EF or SQL Server. Most of the time is spend in

Microsoft.AspNetCore.Mvc.Core.dll!Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor

As you can see in the following image : image Update I later found out that in the Windows performance analyzer doesn't seam to inclued the compiled OData model validator expressions. which are many more in between that nested stack (which is a lot longer than in the image)

Why is "compiling" and validating the OData query so slow ?

Assemblies affected

Microsoft.AspNetCore.OData 7.0.1

Reproduce steps

Create a simple query like the following : /odata/Customers?$filter=contains(Name,'a') and ClientId eq ${ClientId}&$select=Id,Name and run 200 concurrent calls over it with different ClientId as parameter.

Expected result

I would expect better performance with OData since the OData query does not select all columns and is not a complex query.

robward-ms commented 6 years ago

@ovidiubuligan - Can you provide a sample of your model and controller?

ovidiubuligan commented 6 years ago

@robward-ms hello, I created a sample project from scratch here : https://github.com/ovidiubuligan/ODataEF_QueryPerformance_Sample Tere is still a performance difference of 3x with OData. even with retrieving a little less data. (Select 2 columns). In the original case with 10x worse performance the selected columns was still 2 instead of a lot more. So I would expect OData to shine there.

I forgot to add that If you want to run the Jmeter tests you must set the actual port in the jmeter jmx file (i'm new to jmeter).
To run jmeter without GUI run the following command bin\jmeter.bat -n -t SampleODataTest\REST_API_SImpleSearch.jmx.

I am hoping it is just a bug in the AspnetCore pipeline integration with odata and not a general WebApi.OData expected cpu performance inpact.

ovidiubuligan commented 6 years ago

Important Update. I Fiddled around to see why the 10x difference in my case . I commented out all th aggregate roots except the one i tested in my case like so :


            ODataConventionModelBuilder builder = new ODataConventionModelBuilder(services);
            builder.EntitySet<A>("A");
            //builder.EntitySet<B>("B");
            //builder.EntitySet<C>("C");
            //builder.EntitySet<D>("D");                                                    
            //builder.EntitySet<E>("E");
            //builder.EntitySet<F>("F");
            //builder.EntitySet<G>("G");
            //builder.EntitySet<H>("H");
            //builder.EntitySet<I>("I");

These are just the ones I added (Not all entities). and the performance skyrocketed . It went from 10 slower to a bit better than the REST counterpart.

My model entities have some navigations to eachother (As a normal EF model , it is simpler than Adventureworks model). It seams the query validation performance is linked to OData Model complexity but in an quadratic/exponential way.

I also did some more tests with the latest feature/netcore build of AspNetCore.OData with the 3 entity sample i linked in my previews comment and the results are as follows(2000 concurrent requests burst):


Normal REST Test
    7.0.0-beta1
    summary =   2000 in 00:00:07 =  295.2/s Avg:  2918 Min:   143 Max:  5024 Err:     0 (0.00%)

    feature/netcore(latest,debug) 
    summary =   2000 in 00:00:03 =  601.5/s Avg:  1310 Min:    36 Max:  2280 Err:     0 (0.00%)

OData Test
         feature/netcore(latest,debug) 
         summary =   2000 in 00:00:17 =  117.8/s Avg:  8282 Min:   451 Max: 15121 Err:     0 (0.00%)

Is this normal ? What can I do about it ?

arspace commented 5 years ago

hi @ovidiubuligan i'm in the same situation with u. did u find any solution?

raulandresduque commented 3 years ago

Same issue, any suggestion?

spierr commented 3 years ago

Same issue... Using Odata v7.5.0 and Net Core 3.1