I’m inserting from a batch execution around 2100 items in a cosmos DB. Those item represent a product detail taken from a supplier.
I’m saving them in a CosmosDB with the following code
private async Task SaveProductToDbAsync(IList<ResponseItem> products, ILogger log, CancellationToken cancellationToken)
{
using var client = new CosmosClient(_cosmosDbOptions.CosmosDbAccountEndpoint, _cosmosDbOptions.CosmosDbAccountKey);
var database = client.GetDatabase(_cosmosDbOptions.CosmosDbDatabaseName);
var container = database.GetContainer(_cosmosDbOptions.CosmosDbContainerId);
int count = 0;
int successInsert = 0;
foreach (var item in products)
{
item.id = Utility.ToGuid(item.pk);
await container.CreateItemAsync(item, new PartitionKey(item.id), cancellationToken: cancellationToken)
.ContinueWith(itemResponse =>
{
if (itemResponse.IsCompletedSuccessfully)
{
successInsert++;
log.LogDebug($"CosmosDB Created item in Database with id: {item.id}");
}
else
{
AggregateException innerExceptions = itemResponse.Exception.Flatten();
if (innerExceptions.InnerExceptions.FirstOrDefault(
innerEx => innerEx is CosmosException) is CosmosException cosmosException)
{
log.LogError(
$"CosmosDB Could not Create item in Database with id: {item.id}, StatusCode: {cosmosException.StatusCode}, Error: {cosmosException.Message}");
}
else
{
log.LogError(
$"CosmosDB Could not Create item in Database with id: {item.id}, Error: {innerExceptions.InnerExceptions.FirstOrDefault()}");
}
}
}, cancellationToken);
//This is to avoid 429 CosmosDB error messages
await Task.Delay(80, cancellationToken);
count++;
}
log.LogInformation($"Inserted {successInsert}/{count} products");
}
If I don’t put the Task.Delay(80) I got a ton of 429 errors.
But at the end of execution there’re almost 3 minutes wasted waiting.. I’m running this code in a WebJob.
3
Answers
Add throttling-retry settings when you init your CosmosClient like this:
This way, the client will handle 429-retries internally for you and only error out once the defined threshold have been exceeded.
You don’t need to guess how long to call Task.Delay. The Cosmos exception tells you.
As answered @MarkBrown and @Silent, you should use the RetryAfter property and configure MaxRetry properties to prevent looping uselessly.
I had implementation that worked around this point :
Note that I also catch 408 issue that may happen if you overuse that CosmosDb, comparing his scaling and your usage.
And configuration bellow :