Ovladač Mongo vám řekne, které záznamy obsahují chyby a které nebyly zpracovány vůbec. Pokud všechny chyby (obvykle jedna) mají kód 16500, pak je vaším problémem omezení a opakování chyb a zbývající záznamy jsou bezpečné. V opačném případě jsou vaše chyby způsobeny něčím jiným a měli byste provést analýzu a rozhodnout se, zda pokračovat v opakováních.
Ovladač Mongo nevrací hlavičku HTTP, kde Cosmos DB navrhuje zpoždění před opakováním, ale to není velký problém. Zpoždění stejně nezaručuje úspěch, protože jiné požadavky zasahující stejnou databázi mohou spotřebovat RU. Je lepší experimentovat a určovat si vlastní pravidla opakování. Níže je jednoduché rekurzivní řešení, které se neustále opakuje, dokud není vše v pořádku nebo dokud není dosaženo limitu opakování.
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);
}
}