CosmosDB中的跨分区分页

时间:2019-04-16 16:05:03

标签: c# azure-cosmosdb

我有一个CosmosDB集合,该集合已分区并且吞吐量设置为10,000 RU / s(当吞吐量低于6100 RU / s时,不会不会出现此问题)。

现在,我使用变量pageSizecontinuationToken(最初设置为null)发出任意文档查询(例如,检索集合中的所有文档):

var q = DocumentClient.CreateDocumentQuery<T>(CollectionUri,
            new FeedOptions
            {
                MaxItemCount = pageSize,
                EnableCrossPartitionQuery = true,
                RequestContinuation = continuationToken
            });

现在,如果我打电话

  FeedResponse<T> response = await q.ExecuteNextAsync<T>();

我希望根据指定的responsepageSize进行分页。特别是,如果是pageSize = -1pageSize = int.MaxValue,我只希望返回所有结果的一页。但是,结果页面沿分区碎片化。

例如,使用pageSize = -1pageSize = int.MaxValue,我将从第一个分区中获取包含18个对象的页面,只有当第二次调用ExecuteNextAsync时,我才能获取剩余的内容来自其他两个分区的35个对象。

使用pageSize = 17,我将首先在ExecuteNextAsync的第一次调用中得到一个包含17个对象的页面,然后在下一次调用中获得一个具有1个对象的页面,然后是另一个具有17个对象的页面! >

但是,这使分页(几乎)完全无用!还是有一种方法可以正确实现分页(即使吞吐量超过6000 RU / s以上)?

2 个答案:

答案 0 :(得分:0)

MaxItemCount表示对分区的单个请求将返回的最大数据。不能总是这样,有时甚至是空的。

因此,您应该将MaxItemCount排除在分页逻辑之外,因为它与您要实现的目标无关。

相反,您真正想要的是以下内容:

这是一个带有pageSizenextPageToken组合的实现。连续令牌位于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}}是内部克隆的,因此它实际上是一个私有实例。