chuckd
chuckd

Reputation: 14610

Where to catch exceptions in an asynchronous Azure function

I've set up an Azure function and I want it to run asynchronously because I expect to have hundreds/thousands/more messages in my queue that will all get dequeued at the same time, so I've implemented it as such below (maybe there's a better way). Or do I need to worry about running code in the functions asynchronously?

Will Azure handle thousands of these functions run at the same time, if thousands of messages in the queue are all dequeued at once? This Azure function article says only a few hundred can run at once

Where is the best place to put the try catch statement? Inside the asynchronous call around my logic or outside the asynchronous call like my code? Or does it matter?

public static class CancelEvent
{
    [FunctionName("CancelEvent")] 
    public static async void RunAsync([ServiceBusTrigger("canceleventqueue", AccessRights.Manage, Connection = "service_bus_key")]string myQueueItem, TraceWriter log, ExecutionContext context)
    {
        try
        {
            await Task.Run(() => Processor.ProcessAsync());
        }
        catch(Exception ex)
        {
        }
     }
}

public class Processor
{
    public static void ProcessAsync()
    {
        // do the work
    }
}

Upvotes: 2

Views: 4170

Answers (2)

David W
David W

Reputation: 141

TLDR; don't build your Azure Functions to be async.

In my recent experience with azure functions, I found it best NOT to build my functions as asynchronous.

In my situation, I was using a ServiceBusTrigger to receive a message, and then was awaiting a processing method, and was baffled that when bubbling an exception out it would not show up in the azure portal as failed, nor would it capture the exception detail in the Application Insights account, nor would it properly abandon or deadletter the message. I would only discover the exception detail by going to the files\eventlog.xml section in the storage account where I would find a complaint about an unhandled exception bringing the function down to its knees.

After a day spent going down the road of doing a full try/catch and handling message.Abandon() and message.Deadletter() logic myself as well as logging all the AppInsights telemetry manually, I discovered that simply NOT doing async and bubbling out he exception (after first performing error reporting or handling logic) resulted in all the behavior I expected out of the platform.

The 'run history' of the function in the portal correctly showed passed and failed executions, and all the exception detail was captured.

This is a cautionary tale to keep your Azure functions static, synchronous, and simple. The message-handling nature of their design should already be async enough.

Upvotes: 7

Mikhail Shilkov
Mikhail Shilkov

Reputation: 35154

Adding await Task.Run around the call to a synchronous method doesn't make much sense. Strive to make ProcessAsync really async (returning Task and non-blocking).

So the better option (note that both methods return Task):

[FunctionName("CancelEvent")] 
public static async Task RunAsync([ServiceBusTrigger("canceleventqueue", AccessRights.Manage, Connection = "service_bus_key")]string myQueueItem, TraceWriter log, ExecutionContext context)
{
    try
    {
        await Processor.ProcessAsync();
    }
    catch (Exception ex)
    {
        // Try catch will work fine 
    }
}

public class Processor
{
    public static async Task ProcessAsync()
    {
        // do the work
    }
}

and the worse option, but also viable:

[FunctionName("CancelEvent")] 
public static void RunAsync([ServiceBusTrigger("canceleventqueue", AccessRights.Manage, Connection = "service_bus_key")]string myQueueItem, TraceWriter log, ExecutionContext context)
{
    try
    {
        Processor.Process();
    }
    catch (Exception ex)
    {
        // Try catch will work fine 
    }
}

public class Processor
{
    public static void Process()
    {
        // do the work
    }
}

Azure will run multiple executions in parallel in both cases. It's just that the first option will be more lean in resource usage.

Not all messages will be processed at once, but Azure will scale the amount of parallel executions based on some internal scaling logic.

Upvotes: 3

Related Questions