Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Azure Storage Tables: await table.ExecuteAsync(InsertOperation) executes, but never finishes

Well, my problem is that calling await table.ExecuteAsync(...) on Azure Storage Table does insert the requested data, but never finishes (does not return TableResult). Same case with InsertOrUpdate and Update operations. I also tried different tables with different number of properties - same problem.

When I call table.Execute(...) - everything works fine for every kind of operation.

Here's my code - simple as that:

Outer call (it is placed in MVC Controller in async action):

List<Task<ServiceResult<Boolean?>>> addPostTasks = new List<Task<Common.ServiceResult<bool?>>>();              
foreach (var userStream in userStreams)
{
    Task<ServiceResult<Boolean?>> addPostTask = postsStorageSvc.AddImagePost(...);
    postsAddImagePostTasks.Add(addPostTask);
}
Task.WaitAll(addPostTasks.ToArray());

Method called:

public async Task<ServiceResult<Boolean?>> AddImagePost(...)
{
ServiceResult<Boolean?> result = new ServiceResult<bool?>(null);
try
{
    PostTableEntity newPost = new PostTableEntity(streamId.ToString(), Guid.NewGuid().ToString(), creatorId, date, htmlText);              
    TableOperation insertOperation = TableOperation.Insert(newPost);
    //Following line never ends!
    TableResult tableResult = await this._storageTableBootstrapper.Table.ExecuteAsync(insertOperation);                
    //Following line works perfect - but is not ASYNC
    TableResult tableResult = this._storageTableBootstrapper.Table.Execute(insertOperation);
}
catch (Exception ex)
{
    result.Result = false;
    result.Errors.Add("AzurePostsStorageService Unexpected error: " + ex.Message);
}
return result;
}
like image 476
Krzysztof Rudnicki Avatar asked Jan 28 '15 10:01

Krzysztof Rudnicki


2 Answers

The problem is here:

Task.WaitAll(addPostTasks.ToArray());

Your async method tries to marshal itself back to the ASP.NET synchronization context, which is stuck because you initiated a blocking call using Task.WaitAll.

Instead, you'll need to follow the async all the way pattern and use Task.WhenAll, and await on that:

await Task.WhenAll(addPostTasks.ToArray);

Stephan Cleary elaborates on this in his blog post (which @NedStoyanov added):

One other important point: an ASP.NET request context is not tied to a specific thread (like the UI context is), but it does only allow one thread in at a time. This interesting aspect is not officially documented anywhere AFAIK, but it is mentioned in my MSDN article about SynchronizationContext.

like image 199
Yuval Itzchakov Avatar answered Sep 28 '22 14:09

Yuval Itzchakov


This is a classic deadlock caused by this line:

Task.WaitAll(addPostTasks.ToArray());

try changing it to:

await Task.WhenAll(addPostTasks.ToArray());

Basically the the Task.WaitAll blocks the request thread and it is unable to execute the continuation of the Tasks initiated by await table.ExecuteAsync(...). Another alternative is to use ConfigureAwait(false) on your internal tasks to avoid switching SynchronizatonContext.

await table.ExecuteAsync(...).ConfigureAwait(false);

You can use ConfigureAwait(false) whenever you do not require to switch to the original SynchronizationContext. In your case I believe you can do that with all awaits as you are on the server and it shouldn't matter if the code after await executes on the thread pool or not.

See this article for more details: msdn.microsoft.com/enus/magazine/jj991977.aspx

like image 34
NeddySpaghetti Avatar answered Sep 28 '22 13:09

NeddySpaghetti