2
votes

I have an Azure function setup which. Its trigger is set to a Service Bus Queue message. When Function is triggered the message is processed in some way. If it encounters any exception while processing I handle that error and save the message in Azure Table Storage set as output Binding for logging purposes.

I want to handle any errors thrown when writing something to the output binding. How would I go about doing that?

Detail of the Error

One of the errors I encountered during writing to output binding (table storage) is that value of some key cannot be saved. For e.g. one of the key in the message is time with value of type number. When writing this data, I got exception that the key's value cannot fit in Int32 number. I think table storage tried to convert the number to Int32 by default but failed. To workaround this I converted all keys to have string values. Now I can save.

But still I want to handle any unforeseeable errors while writing to table storage output binding.

Handling error is important in this case, because if the Service Bus Message is not properly consumed, then message is not deleted in Service Bus Queue, and after completion of Azure Function execution, that message again triggers the Function, and Function enters an indefinite loop.

Following is a sample of the Azure function

module.exports = async function (context, mySbMsg) {
    try{
        // process service bus message
        await processMsg(mySbMsg);
    } catch(e) {
        // if processing fails, save the message in Azure table
        try{
            // my own try to handle errors, but was unsuccessful
            await new Promise(function (resolve, reject) {
                context.bindings.tableBinding = [];
                context.bindings.tableBinding.push({
                    PartitionKey: mySbMsg.id|| "unknown",
                    RowKey: time + "",
                    errorMessage: e.message || "",
                    ...mySbMsg
                })
                resolve();
            });
        } catch (e) {
            // do something HERE;
            // exception on output binding didn't brought me here
        }
    }
    context.done();
}
1

1 Answers

2
votes

The input and output binding execute outside of the scope of the function, which is why you can't enter catch blocks within the function. If you want to catch the error you can handle the write to storage yourself instead of using an output binding.

However, your Service Bus queue will not trigger the function in an infinite loop if you fail to write to storage. Service Bus limits the number of retries and once that limit is hit, the message is moved to a seperate dead letter queue to avoid infinite loops. The messages in there don't expire- they will sit there until you are ready to process them or delete them.

enter image description here

The default maximum delivery count is 10 and you can configure this on the queue's properties blade.

As far as dealing with the messages in the dead letter queue, I've found Service Bus Explorer to be very helpful. It makes it fairly straightforward to take a look at the messages in the DLQ, and then either push them back into the main queue or delete them.

You can also access the DLQ progromatically and build automated systems to deal with these messages. However, since there is nowhere for the messages to go from there, you can get into a loop if your DLQ handler fails to resolve the messages.