.net 4.7.2 ~10 times worse performance than .net core (or old v2 sdk with 4.7.2)
See original GitHub issueDescribe the bug On .net 4.7.2 we get horrible performance using the new v3 library. The same Linq query that matches all Documents where one varible is true takes 80-200ms with .net core and over a full second with .net, it also takes only around 150ms with the old v2 library using .net 4.7.2.
To Reproduce We have been using a Repository pattern approach with a generic static class.
Shrunken down Repository
public class CosmosDbRepository<T> where T : EntityBase
{
private static CosmosClient client;
private static Database database;
private static Container container;
private const string DEFAULT_PARTITION_KEY = "/_partitionKey";
private const int DEFAULT_REQUEST_UNITS = 400;
public static void Initialize(
string collection = null,
string partitionKeyPath = DEFAULT_PARTITION_KEY,
int requestUnits = DEFAULT_REQUEST_UNITS,
string endpoint = null,
string authKey = null,
string databaseName = null)
{
var options = new CosmosClientOptions()
{
ConnectionMode = ConnectionMode.Direct
};
client = new CosmosClient(
endpoint ?? "<insert_endpoint>",
authKey ?? "<insert_api_key>",
clientOptions: options
);
database = client.CreateDatabaseIfNotExistsAsync(databaseName ?? "<insert_db>").Result;
container = database.CreateContainerIfNotExistsAsync(collection ?? typeof(T).Name, partitionKeyPath, requestUnits).Result;
}
public static async Task<IEnumerable<T>> QueryAsync(
Expression<Func<T, bool>> whereExpr,
Expression<Func<T, object>> orderExpr = null,
SortOrder sortOrder = SortOrder.Descending,
PartitionKey? partitionKey = null,
int? take = null)
{
var iterator = ConstructQueryable(
whereExpr: whereExpr,
orderExpr: orderExpr,
sortOrder: sortOrder,
partitionKey: partitionKey,
take: take
).ToFeedIterator();
var results = new List<T>();
while (iterator.HasMoreResults)
{
var result = await iterator.ReadNextAsync();
Console.WriteLine($"Result count: {result.Count} Used up: {result.RequestCharge} RUs");
results.AddRange(result.Resource);
}
return results;
}
private static IQueryable<T> ConstructQueryable(
Expression<Func<T, bool>> whereExpr = null,
Expression<Func<T, object>> orderExpr = null,
int? maxItemCount = null,
int? take = null,
string continuationToken = null,
SortOrder sortOrder = SortOrder.Descending,
PartitionKey? partitionKey = null)
{
var options = new QueryRequestOptions()
{
PartitionKey = partitionKey,
MaxItemCount = maxItemCount,
MaxConcurrency = -1,
MaxBufferedItemCount = -1,
EnableScanInQuery = true
};
IQueryable<T> queryable = container.GetItemLinqQueryable<T>(
requestOptions: options,
continuationToken: continuationToken,
allowSynchronousQueryExecution: true);
if (orderExpr != null)
{
if (sortOrder == SortOrder.Descending)
queryable = queryable.OrderByDescending(orderExpr);
else
queryable = queryable.OrderBy(orderExpr);
}
if (whereExpr != null) queryable = queryable.Where(whereExpr);
if (take != null) queryable = queryable.Take(take.Value);
return queryable;
}
}
Base class for DocumentDB Entities/Documents.
public abstract class EntityBase
{
[JsonProperty(PropertyName = "id")]
public string DocumentId { get; set; }
[JsonIgnore]
public virtual PartitionKey PartitionKey { get { return PartitionKey.None; } }
}
Test Console Application
static void Main(string[] args)
{
CosmosDbRepository<SomeClassThatDerivesEntityBase>.Initialize();
var results = CosmosDbRepository<SomeClassThatDerivesEntityBase>.QueryAsync(
whereExpr: x => x.ActiveDie).Result;
var sp = new Stopwatch();
for (int i = 0; i < 10; i++)
{
sp.Start();
results = CosmosDbRepository<SomeClassThatDerivesEntityBase>.QueryAsync(
whereExpr: x => x.SomeBooleanProperty).Result;
sp.Stop();
Console.WriteLine($"This took {sp.ElapsedMilliseconds}ms.");
sp.Reset();
}
Console.ReadLine();
}
I can´t provide you with the classes I used or the documents. But it is a relatively big class with 27 properties and 2 of them being other classes.
Output of that Console Application
.net core
Result count: 65 Used up: 9,05 RUs
Result count: 65 Used up: 9,05 RUs
This took 284ms.
Result count: 65 Used up: 9,05 RUs
This took 276ms.
Result count: 65 Used up: 9,05 RUs
This took 92ms.
Result count: 65 Used up: 9,05 RUs
This took 88ms.
Result count: 65 Used up: 9,05 RUs
This took 81ms.
Result count: 65 Used up: 9,05 RUs
This took 285ms.
Result count: 65 Used up: 9,05 RUs
This took 78ms.
Result count: 65 Used up: 9,05 RUs
This took 83ms.
Result count: 65 Used up: 9,05 RUs
This took 85ms.
Result count: 65 Used up: 9,05 RUs
This took 78ms.
.net 4.7.2
Result count: 65 Used up: 9,05 RUs
Result count: 65 Used up: 9,05 RUs
This took 1147ms.
Result count: 65 Used up: 9,05 RUs
This took 968ms.
Result count: 65 Used up: 9,05 RUs
This took 1113ms.
Result count: 65 Used up: 9,05 RUs
This took 971ms.
Result count: 65 Used up: 9,05 RUs
This took 924ms.
Result count: 65 Used up: 9,05 RUs
This took 939ms.
Result count: 65 Used up: 9,05 RUs
This took 1144ms.
Result count: 65 Used up: 9,05 RUs
This took 943ms.
Result count: 65 Used up: 9,05 RUs
This took 957ms.
Result count: 65 Used up: 9,05 RUs
This took 966ms.
Expected behavior For it to be close/match/exceed to the performance of the old v2 library on .net 4.7.2 or drop support for .net. Because, if the performance is like that it is not feasable to use it. You should make people use the v2 library then. Because otherwise they will have a bad experience. It is quite literally 10 times slower than the old v2 library, but uses the exact same RUs.
Environment summary SDK Version: 3.1.1 OS Version: Windows 10 18362
Additional Information
I also tried to do a SQL query, because I initially thought the LINQ implementation might not be very good (yet). The results are similar.
I also tried it with the StreamingAPI now it takes just about the same amount of time. Even if I skip JSON parsing all together and just read the strings into a List. It still takes roughly 1 second to execute, it might be bit faster (didn´t compare exactly). Point is that is not why it´s that much slower.
Issue Analytics
- State:
- Created 4 years ago
- Comments:29 (14 by maintainers)
Top GitHub Comments
suggest reposting those with a release build run with ctrl-F5. There’s no point in doing any perf numbers on a debug build and/or with a debugger attached
It’s worth mentioning the quality/nature of the network when doing so - i.e. running in a VM in the data centre would be optimal (and would pretty much exclude the network as a factor) but not necessary (and of course the connection mode, which becomes more significant when you’re measuring the real thing)