使用insertMany

时间:2019-03-20 14:59:50

标签: c# mongodb azure azure-cosmosdb

我有以下存储库类,可从批处理中将数据插入CosmosDb数据库:

public bool InsertZonierData(List<Zonier> zonierList)
{
    if (zonierList == null || !zonierList.Any())
    {
        throw new ZonierListNullOrEmptyException();
    }
    else
    {
        try
        {
            _collection.InsertMany(zonierList);
            return true;
        }
        catch (MongoBulkWriteException ex)
        {
            throw new DataBaseWritingException(ex.Message, ExceptionCodeConstants.DataBaseWritingExceptionCode);
        }
    }
}

不幸的是,zonierList中有超过30000个元素,它在CosmosDb上引发了以下异常:

  

未处理的异常:MongoDB.Driver.MongoCommandException:命令插入失败:消息:{“错误”:[“请求率很大”]}

根据文档,这是与Cosmos上的RU /秒相关的问题。当然,简单的方法是增加它,但这不是我想要的。

是否有一种简单明了的方法来重构该方法,使我们能够在不中断CosmosDb 400 RU /秒的情况下插入数据。

3 个答案:

答案 0 :(得分:1)

mongo sdk完全不知道CosmosDB的存在。这意味着它没有任何限制请求的重试逻辑。这意味着,如果您希望将RU保持在400,则必须对列表进行批处理,并使用客户端限制机制来调用insertmany方法。

您可以通过获取每个文档的大小来计算,将其乘以10(这是1kb文档的插入费用),然后编写一段代码,该代码根据该文档的大小进行批处理并每秒执行一次。

答案 1 :(得分:1)

Mongo驱动程序会告诉您哪些记录出错,而哪些记录根本没有处理。如果所有错误(通常是一个)的代码都为16500,那么您的问题就在节流并重试错误,其余记录是安全的。否则,您的错误是由其他原因引起的,应该进行分析并决定是否继续重试。

Mongo驱动程序不会返回HTTP标头,而Cosmos DB建议该标头在重试之前延迟,但这没什么大不了的。延迟仍然不能保证成功,因为其他命中相同数据库的请求可能会用完RU。您最好尝试并确定自己的重试规则。下面是一个简单的递归解决方案,它可以不断重试直到一切正常或达到重试限制。

    private async Task InsertManyWithRetry(IMongoCollection<BsonDocument> collection, 
        IEnumerable<BsonDocument> batch, int retries = 10, int delay = 300)
    {
        var batchArray = batch.ToArray();

        try
        {
            await collection.InsertManyAsync(batchArray);
        }
        catch (MongoBulkWriteException<BsonDocument> e)
        {
            if (retries <= 0)
                throw;

            //Check if there were any errors other than throttling.
            var realErrors = e.WriteErrors.Where(we => we.Code != 16500).ToArray();
            //Re-throw original exception for now.
            //TODO: We can make it more sophisticated by continuing with unprocessed records and collecting all errors from all retries.
            if (realErrors.Any())
                throw;

            //Take all records that had errors.
            var errors = e.WriteErrors.Select(we => batchArray[we.Index]);
            //Take all unprocessed records.
            var unprocessed = e.UnprocessedRequests
                .Where(ur => ur.ModelType == WriteModelType.InsertOne)
                .OfType<InsertOneModel<BsonDocument>>() 
                .Select(ur => ur.Document);

            var retryBatchArray = errors.Union(unprocessed).ToArray();

            _logger($"Retry {retryBatchArray.Length} records after {delay} ms");

            await Task.Delay(delay);

            await InsertManyWithRetry(collection, retryBatchArray, retries - 1, delay);
        }
    }

答案 2 :(得分:0)

我通过使用mongo api对cosmos bs使用重试逻辑解决了该问题。 您可以根据需要应用延迟。

={'Sheet2'!X:X}