我有一个CosmosDB集合,该集合已分区并且吞吐量设置为10,000 RU / s(当吞吐量低于6100 RU / s时,不会不会出现此问题)。
现在,我使用变量pageSize
和continuationToken
(最初设置为null
)发出任意文档查询(例如,检索集合中的所有文档):
var q = DocumentClient.CreateDocumentQuery<T>(CollectionUri,
new FeedOptions
{
MaxItemCount = pageSize,
EnableCrossPartitionQuery = true,
RequestContinuation = continuationToken
});
现在,如果我打电话
FeedResponse<T> response = await q.ExecuteNextAsync<T>();
我希望根据指定的response
对pageSize
进行分页。特别是,如果是pageSize = -1
或pageSize = int.MaxValue
,我只希望返回所有结果的一页。但是,结果页面沿分区碎片化。
例如,使用pageSize = -1
或pageSize = int.MaxValue
,我将从第一个分区中获取包含18个对象的页面,只有当第二次调用ExecuteNextAsync
时,我才能获取剩余的内容来自其他两个分区的35个对象。
使用pageSize = 17
,我将首先在ExecuteNextAsync
的第一次调用中得到一个包含17个对象的页面,然后在下一次调用中获得一个具有1个对象的页面,然后是另一个具有17个对象的页面! >
但是,这使分页(几乎)完全无用!还是有一种方法可以正确实现分页(即使吞吐量超过6000 RU / s以上)?
答案 0 :(得分:0)
MaxItemCount
表示对分区的单个请求将返回的最大数据。不能总是这样,有时甚至是空的。
因此,您应该将MaxItemCount
排除在分页逻辑之外,因为它与您要实现的目标无关。
相反,您真正想要的是以下内容:
这是一个带有pageSize
和nextPageToken
组合的实现。连续令牌位于FeedOptions
的{{1}}中;
query
要使其在任何RU / s上都有效,您将需要用重试包装器包装var results = new List<T>();
var nextPageToken = string.Empty;
while (query.HasMoreResults)
{
if (results.Count == pageSize)
break;
var items = await query.ExecuteNextAsync<T>(cancellationToken);
nextPageToken = items.ResponseContinuation;
foreach (var item in items)
{
results.Add(item);
if (results.Count == pageSize)
break;
}
}
return (results, nextPageToken);
调用,或者简单地增加DocumentClient的重试选项。
有关进一步的实现细节,您可以查看Cosmonaut如何处理分页并解决此问题,尤其是here。 (完全公开,我是该库的创建者,但我不想在此处粘贴完整的实现)
答案 1 :(得分:0)
根据尼克·查普萨斯(Nick Chapsas)的信息,即使有更多可用的商品,ExecuteNextAsync
可能返回的商品少于MaxItemCount
,我正在使用以下解决方法:
List<T> result = new List<T>();
string continuationToken = null;
IDocumentQuery<T> docQuery = queryable.AsDocumentQuery();
// ugly hack to get the feed options using reflection
FeedOptions feedOptions = docQuery.GetNonPublicProperty<FeedOptions>("feedOptions");
while (docQuery.HasMoreResults && (pageSize <= 0 || result.Count < pageSize))
{
if (feedOptions != null && pageSize > 0)
{
feedOptions.MaxItemCount = pageSize - result.Count;
}
FeedResponse<T> response = await docQuery.ExecuteNextAsync<T>();
result.AddRange(response.ToList());
continuationToken = response.ResponseContinuation;
}
return (result, continuationToken);
使用反射来获取私有属性不是很好,但是似乎没有其他方法来获取查询的FeedOptions
。特别是,用于调用FeedOptions
的{{1}}是内部克隆的,因此它实际上是一个私有实例。