Search code examples
c#asp.netasync-awaitdeadlock

Struggling to get async working on deployment in ASP.net


The code works fine on my development environment, but in deployment with scallable architecture it appears to deadlock.

Objective here is to take a queue of API requests to send to SendGrid, batch them up and process each batch one at a time.

First call from ASHX handler

public void ProcessRequest(HttpContext context)
{
    var result = Code.Helpers.Email.Sendgrid.Queue.Process().Result;
    if (result.Success)
    {

Queue.Process()

public static async Task<GenericMethodResult> Process()
{
    var queueItems = GetQueueItemsToProcess();
    var batches = BatchQueueItems(queueItems);

    foreach (var batch in batches)
    {
        var r = await batch.SendToSendGrid();
        if (r.StopBatch)
        {
            break;
        }
    }

    return new GenericMethodResult(true);
}

SendToSendGrid()

public async Task<SendGridAPIMethodResponse> SendToSendGrid()
{
    var r = new SendGridAPIMethodResponse();
    var json = API.Functions.CreateJSONData(this);
    var sg = new SendGridClient(Settings.Email.SendgridAPIKey);

    dynamic response;
    if (Action == Action.UpdateRecipient)
    {
        response = await sg.RequestAsync(SendGridClient.Method.PATCH, urlPath: "contactdb/recipients", requestBody: json);
    }

    string jsonResponse = response.Body.ReadAsStringAsync().Result;
    // Process response...

    return r;
}

I've stripped out as much of the code as I could.

Is anyone able to tell me why this code is timing out in production?


Solution

  • This blocking call to .Result in SendToSendGrid() is causing a deadlock as you are mixing async and blocking calls.

    string jsonResponse = response.Body.ReadAsStringAsync().Result;
    

    Use async all the way through

    var jsonResponse = await response.Body.ReadAsStringAsync();
    

    and try to avoid mixing blocking calls in async methods.

    You should also conside making your handler async as well by using HttpTaskAsyncHandler.

    public class MyHandler : HttpTaskAsyncHandler {
        public override async Task ProcessRequestAsync(HttpContext context) {
            var result = await Code.Helpers.Email.Sendgrid.Queue.Process();
            if (result.Success) {
                //..other code
            }
        }
    }