I'm trying to use the async methods to upload a file to Azure blob storage and then set its metadata, but the UploadFromByteArrayAsync
method never returns.
I have the following code:
var connAzureBlob = ConfigurationManager.AppSettings["AzureBlobStorage"];
var storageAccount = CloudStorageAccount.Parse(connAzureBlob);
var blobClient = storageAccount.CreateCloudBlobClient();
var fileContainer = blobClient.GetContainerReference(ConfigurationManager.AppSettings["AzureBlobContainer"]);
if (!fileContainer.Exists())
{
await fileContainer.CreateAsync();
await fileContainer.SetPermissionsAsync(new BlobContainerPermissions {
PublicAccess = BlobContainerPublicAccessType.Blob
});
}
try
{
var fileBlob = fileContainer.GetBlockBlobReference(documentId.ToString());
await fileBlob.UploadFromByteArrayAsync(buffer, 0, buffer.Length);
Log.Info($"{nameof(SaveToBlobAsync)}: blob {documentId} uploaded.");
await fileBlob.FetchAttributesAsync();
fileBlob.Properties.ContentType = contentType;
fileBlob.Metadata["..."] = "...";
await fileBlob.SetMetadataAsync();
Log.Info($"{nameof(SaveToBlobAsync)}: {documentId} - metadata saved.");
}
catch (Exception exception)
{
Log.Error($"{nameof(SaveToBlobAsync)}: An exception was thrown while saving a file to a blob: ", exception);
throw;
}
Using the above code, I'd expect to see the following messages logged:
SaveToBlobAsync: blob 123 uploaded.
SaveToBlobAsync: 123 - metadata saved.
But these never appear.
However, the blob does appear to be stored (I can view its contents using the Azure Storage Explorer) but the process doesn't seem to move to the next line (which would be the first logging message).
If I switch all the async calls for their non-async counterparts then the code works as expected.
Can anyone explain why UploadFromByteArrayAsync
doesn't seem to return?
Update: I thought I'd add some more information about the context, in case it helps.
This code is being called from a Web API method. The controller in question has no async code and does little else other than call a repository method. The repository method updates a SQL DB - again, all this is non-async code - before calling a class which interfaces with the blob storage.
This blob storage-facing class has non-async methods which do little else other than calling their async equivalents with either .Wait()
or .Result
.
In the case in question, the repository in the second stage is calling the non-async version, so the above code is essentially being called with .Wait()
.
Until the above code, there are no other async calls in this HTTP request from the point where it enters the Web API's controller.
Running AzCopy without specifying the option /SyncCopy and running PowerShell command Start-AzureStorageBlobCopy should take the same duration, because they both use server side asynchronous copy.
A blob or version that has not yet been soft deleted is protected by the immutability policy and cannot be soft deleted until after the time-based retention policy has expired or the legal hold has been removed.
The error is almost certainly further up your call stack, where some method is blocking on the returned task (e.g., Task<T>.Result
, Task.Wait
, etc.). This will deadlock if the thread being blocked is required for the task to complete.
I explain this situation in full on my blog, but the gist of it is this:
await
by default will capture a "context" and use that to resume the async
method.SynchronizationContext
; if this code is executed in a UI thread, this "context" is a UI SynchronizationContext
.SynchronizationContext
and UI SynchronizationContext
only allow one thread in them at a time.Thus, when the calling code blocks on the returned Task
, it is holding a thread within that context, preventing the Task
from completing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With