Prerequisites
- WindowsAzure.Storage V 9.3.3
- Azure.Storage.Blobs V 12.6.0
- Datadog APM monitoring
Context:
- 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 - Due to existed implementation, the
CreateIfNotExists
check is executing per a request, that mean a lot of times - As a result, there are a lot of 409 errors in a log & monitor systems
- 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
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);
}