ExecuteNextAsync
в IDocumentQuery, похоже, не возвращает никаких результатов, даже если HasMoreResults
возвращает true. Последующие вызовы ExecuteNextAsync
неожиданно возвращают данные. Кроме того, ToList()
на IQueryable
возвращает данные последовательно.
Это поведенческое изменение в рабочем коде, которое работало корректно более года, что означает, что ExecuteNextAsync
ранее надежно возвращал результаты.
Я взял свой производственный код и упростил до повторяемого контрольного примера. Композиция запроса, по-видимому, влияет на поведение ExecuteNextAsync
(т. Е. Выполнение одного или нескольких вызовов до возвращения результатов). Например, если я выполняю поиск по id + partitionKey, он работает как положено.
Соответствующие зависимости:
Я исследовал следующее, что не помогло:
Вот минимальный тест, который демонстрирует проблему:
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
using Newtonsoft.Json;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Program.Run(args).GetAwaiter().GetResult();
}
static string endpointUrl = "xxxxx";
static string authKeyOrResourceToken = "xxxxx";
static Uri collectionUri = UriFactory.CreateDocumentCollectionUri("xxxxx", "xxxxx");
static async Task Run(string[] args)
{
ConnectionPolicy connectionPolicy = new ConnectionPolicy();
DocumentClient client = new DocumentClient(new Uri(endpointUrl), authKeyOrResourceToken, connectionPolicy, ConsistencyLevel.BoundedStaleness);
await Program.Test(client);
}
public static async Task Test(DocumentClient client)
{
FeedOptions feedOptions = new FeedOptions()
{
EnableCrossPartitionQuery = true,
MaxDegreeOfParallelism = -1,
MaxBufferedItemCount = 1,
MaxItemCount = 1,
PopulateQueryMetrics = true,
};
StringBuilder sql = new StringBuilder();
sql.AppendLine(" SELECT c.editionBudgetOrderGroupID, c.items, c.artifactType, c.level, c.editionID, c.sectionID, c.parentContentRef");
sql.AppendLine(" FROM c ");
sql.AppendLine(" JOIN items IN c.items ");
sql.AppendLine(" WHERE ");
sql.AppendLine(" c.artifactType = @artifactType ");
sql.AppendLine(" AND items.contentID = @childContentId ");
sql.AppendLine(" AND c.level = @level");
sql.AppendLine(" AND c.tenant = @tenantId");
SqlParameterCollection sqlParams = new SqlParameterCollection
{
new SqlParameter("@artifactType", "EditionBudgetOrderGroup"),
new SqlParameter("@tenantId", "xxx"),
new SqlParameter("@childContentId", "xxxxx"),
new SqlParameter("@level", "ContentItemLevel")
};
SqlQuerySpec sqlSpec = new SqlQuerySpec
{
QueryText = sql.ToString(),
Parameters = sqlParams,
};
// this is ok
Document thisIsOk1 = client.CreateDocumentQuery<Document>(
collectionUri,
sqlSpec,
feedOptions).ToList().FirstOrDefault();
// this is ok
Document thisIsOk2 = client.CreateDocumentQuery<Document>(
collectionUri,
sqlSpec,
feedOptions).AsEnumerable().FirstOrDefault();
// This is normally called by the calling method, value returned is null
IDocumentQuery<Document> result = client.CreateDocumentQuery<Document>(
collectionUri,
sqlSpec,
feedOptions).AsDocumentQuery();
Console.WriteLine($"result.HasMoreResults: {result.HasMoreResults}");
FeedResponse<Document> feedResponse1 = await result.ExecuteNextAsync<Document>();
IList<Document> thisIsNullList = feedResponse1.ToList();
Console.WriteLine($"thisIsNullList?.Count : {thisIsNullList?.Count}");
Document thisIsNull = feedResponse1.FirstOrDefault();
// this is ok - calling ExecuteNextAsync causes expected result
FeedResponse<Document> feedResponse2 = await result.ExecuteNextAsync<Document>();
Document thisIsOk3 = feedResponse2.FirstOrDefault();
Console.WriteLine($"thisIsOk1 == null : {thisIsOk1 == null}");
Console.WriteLine($"thisIsOk2 == null : {thisIsOk2 == null}");
Console.WriteLine($"thisIsNull == null : {thisIsNull == null}");
Console.WriteLine($"thisIsOk3 == null : {thisIsOk3 == null}");
string metrics1 = JsonConvert.SerializeObject(feedResponse1.QueryMetrics, Formatting.Indented);
string metrics2 = JsonConvert.SerializeObject(feedResponse2.QueryMetrics, Formatting.Indented);
Console.WriteLine($"feedResponse1.QueryMetrics: {metrics1}");
Console.WriteLine($"feedResponse2.QueryMetrics: {metrics2}");
}
}
}
result.HasMoreResults: True
thisIsNullList?.Count : 0
Я ожидаю, что thisIsNull
будет иметь значение, поскольку HasMoreResults равно True.
thisIsOk1 == null : False
thisIsOk2 == null : False
thisIsNull == null : True
thisIsOk3 == null : False
Вот метрики запроса для первой и второй операции ExecuteNextAsync
. Иногда вторая операция не имеет показателей, как показано ниже, а иногда она заполняется.
feedResponse1.QueryMetrics: {
"1": {
"TotalTime": "00:00:00.0017400",
"RetrievedDocumentCount": 0,
"RetrievedDocumentSize": 0,
"OutputDocumentCount": 0,
"QueryPreparationTimes": {
"CompileTime": "00:00:00.0001300",
"LogicalPlanBuildTime": "00:00:00.0000700",
"PhysicalPlanBuildTime": "00:00:00.0001400",
"QueryOptimizationTime": "00:00:00.0000100"
},
"QueryEngineTimes": {
"IndexLookupTime": "00:00:00.0011200",
"DocumentLoadTime": "00:00:00",
"WriteOutputTime": "00:00:00",
"RuntimeExecutionTimes": {
"SystemFunctionExecutionTime": "00:00:00",
"UserDefinedFunctionExecutionTime": "00:00:00",
"TotalTime": "00:00:00.0000300"
}
},
"Retries": 0,
"ClientSideMetrics": {
"Retries": 0,
"RequestCharge": 11.83,
"FetchExecutionRanges": [
{
"ActivityId": "660e25e9-0904-4f97-a627-f836422151f3",
"StartTime": "2019-05-01T19:38:07.0354836Z",
"EndTime": "2019-05-01T19:38:07.0642583Z",
"PartitionId": "1",
"NumberOfDocuments": 0,
"RetryCount": 0
}
],
"PartitionSchedulingTimeSpans": [
{
"Item1": "1",
"Item2": {
"NumPreemptions": 1,
"TurnaroundTime": "00:00:00.0289540",
"ResponseTime": "00:00:00.0000617",
"RunTime": "00:00:00.0287753",
"WaitTime": "00:00:00.0001791"
}
}
]
},
"IndexHitRatio": 1.0
},
"2": {
"TotalTime": "00:00:00.0016100",
"RetrievedDocumentCount": 1,
"RetrievedDocumentSize": 1356,
"OutputDocumentCount": 1,
"QueryPreparationTimes": {
"CompileTime": "00:00:00.0001300",
"LogicalPlanBuildTime": "00:00:00.0000800",
"PhysicalPlanBuildTime": "00:00:00.0001500",
"QueryOptimizationTime": "00:00:00.0000200"
},
"QueryEngineTimes": {
"IndexLookupTime": "00:00:00.0009200",
"DocumentLoadTime": "00:00:00.0000300",
"WriteOutputTime": "00:00:00",
"RuntimeExecutionTimes": {
"SystemFunctionExecutionTime": "00:00:00",
"UserDefinedFunctionExecutionTime": "00:00:00",
"TotalTime": "00:00:00.0000500"
}
},
"Retries": 0,
"ClientSideMetrics": {
"Retries": 0,
"RequestCharge": 12.8,
"FetchExecutionRanges": [
{
"ActivityId": "3fc13562-1f3a-4636-ac54-492d01040dcb",
"StartTime": "2019-05-01T19:38:07.035552Z",
"EndTime": "2019-05-01T19:38:07.0634899Z",
"PartitionId": "2",
"NumberOfDocuments": 1,
"RetryCount": 0
}
],
"PartitionSchedulingTimeSpans": [
{
"Item1": "2",
"Item2": {
"NumPreemptions": 1,
"TurnaroundTime": "00:00:00.0281628",
"ResponseTime": "00:00:00.0000935",
"RunTime": "00:00:00.0279391",
"WaitTime": "00:00:00.0002242"
}
}
]
},
"IndexHitRatio": 1.0
}
}
feedResponse2.QueryMetrics: {}