Is adding "Throw;" an appropriate way to cancel an Orchestrator Function Instance when exception is thrown?

215 Views Asked by At

I have an Orchestrator Function (Isolated Worker for .NET 7 web application) which calls multiple Activity Functions. Each of those Activity Functions has their own try/catch block in their respective method bodies. So, if an exception is thrown in an Activity Function or in the Orchestrator Function, they all have their own try/catch block, so the exception is handled. According to the bottom of this documentation:

"If an orchestrator function fails with an unhandled exception, the details of the exception are logged and the instance completes with a Failed status."

Because this Function is triggered by users (internal to our company) and could take 1-2+ hours, I want to have the status updated on an exception which users can do by clicking a button (I have a separate question about asynchronously updating UI from Orchestrator Function if anyone has knowledge of appropriate approach, but that's a separate topic.

So, is it a good idea to just add "Throw" after each exception is handled, including the Orchestrator Function, so the function fails and the status is updated or is that a bad approach/not the best approach?

1

There are 1 best solutions below

0
On BEST ANSWER

Rethrowing an exception is common practice, reference document.

I am rethrowing exception in the activity function to get the error in activity function.

My durable function code:

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;

namespace FunctionApp4
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static async Task<List<string>> RunOrchestrator(
            [OrchestrationTrigger] IDurableOrchestrationContext context)
        { 
           try{
                List<string> outputs = new List<string>();
                outputs.Add(await context.CallActivityAsync<string>(nameof(Activity1), ""));
                outputs.Add(await context.CallActivityAsync<string>(nameof(Activity2), ""));

                return outputs;
               }
            catch (Exception ex)
            {
                Console.WriteLine(ex.StackTrace);
                throw ex;
            }
        }

        [FunctionName(nameof(Activity1))]
        public static string Activity1([ActivityTrigger] string name, ILogger log)
        {
            try
            {
                string str = "Vivek";
                char ch = str[6];
                return str;
            }
            catch (ArithmeticException)
            {
                throw;
            }
        }

        [FunctionName(nameof(Activity2))]
        public static string Activity2([ActivityTrigger] string name, ILogger log)
        {
            try
            {
                string str = "hello";
                char ch = str[5];
                return str;
            }
            catch (ArithmeticException)
            {
                throw;
            }
        }

        [FunctionName("Function1_HttpStart")]
        public static async Task<HttpResponseMessage> HttpStart(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestMessage req,
            [DurableClient] IDurableOrchestrationClient starter,
            ILogger log)
        {
            string instanceId = await starter.StartNewAsync("Function1", null);

            log.LogInformation("Started orchestration with ID = '{instanceId}'.", instanceId);

            return starter.CreateCheckStatusResponse(req, instanceId);
        }
    }
}

Output:

enter image description here

enter image description here