Home > Blockchain >  Alternative for BlobContainerClient.CreateIfNotExistsAsync method to avoid 409 http error
Alternative for BlobContainerClient.CreateIfNotExistsAsync method to avoid 409 http error

Time:09-22

Prerequisites

  • WindowsAzure.Storage V 9.3.3
  • Azure.Storage.Blobs V 12.6.0
  • Datadog APM monitoring

Context:

  1. This is a known problem, that a blob client CreateIfNotExists(Async) returns 409 during execution, in case if a container already has been created before
  2. Due to existed implementation, the CreateIfNotExists check is executing per a request, that mean a lot of times
  3. As a result, there are a lot of 409 errors in a log & monitor systems
  4. Also, it's kind of challenge to move logic to the app root, and, thus, to avoid per request CreateIfNotExists check

The possible fix, under consideration:

To mitigate the issue, I gonna to replace it with something like:

if (!await blobClient.ExistsAsync().ConfigureAwait(false))
{
   await containerClient.CreateAsync().ConfigureAwait(false);
}

Question: And one moment that bothering me:

  • Could be such replacement become a concern within high concurrent workflow ?

P.s I've looked into CreateIfNotExistsAsync implementation at 'Azure.Storage.Blobs' nuget. From my point of view, nothing special about concurrency, but maybe I'm wrong. Pls. share you experience

enter image description here

CodePudding user response:

Your code will have to handle this problem. Checking for existence won't help.

The problem is concurrency on the server side, not concurrency in the client. The source shows thatCretaeIfNotExists already handles the case of an existing container by simply ignoring the exception. One could ask why an exception is thrown instead of checking the status code, but the code doesn't throw a 409 if a container exists:

try
{
    response = await CreateInternal(
        publicAccessType,
        metadata,
        encryptionScopeOptions,
        async,
        cancellationToken,
        operationName)
        .ConfigureAwait(false);
}
catch (RequestFailedException storageRequestFailedException)
when (storageRequestFailedException.ErrorCode == BlobErrorCode.ContainerAlreadyExists)
{
    response = default;
}
catch (Exception ex)
{
    ClientConfiguration.Pipeline.LogException(ex);
    scope.Failed(ex);
    throw;
}
finally
{
    ClientConfiguration.Pipeline.LogMethodExit(nameof(BlobContainerClient));
    scope.Dispose();
}
return response;

For a conflict to occur, two CreateInternal calls would have to try to create the same non-existent container. That's why checking for existence won't help here. Even if an existence check was used, both clients would still try to execute Create at the same time and both would get a 409.

The application will have to handle this case. The best option would be reducing the chance of conflicts by not making this call on every request. This could be done by using eg a Lazy<> or asynchronous Lazy to make this call. Or using queued workers, reducing the number of calls that can happen at the same time.

The application would still have to handle the occasional 409. One way would be to simply retry CreateIfNotExists after a delay, until the call succeeds. That's better than calling Exists because the container's creation could fail.

CodePudding user response:

BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);

            if (!containerClient.Exists())
            {
                containerClient = await blobServiceClient.CreateBlobContainerAsync(containerName, PublicAccessType.BlobContainer);
            }
  • Related