Normally I use, and recommend, Logic Apps for orchestration, but I recently had an ask for sample code of a Durable Function that demonstrated error handling and retry logic.
The code for this article can be found at https://github.com/mattruma/MJR047.
Using the Azure Storage Emulator I created two queues, one called sayhello
and another called sayhello-poison
, the latter will be where we put messages that could not be processed.
In Visual Studio, I created a new C# Azure Function project.
I created a SayHelloRequest class to model an incoming requests.
1 2 3 4 5 |
public class SayHelloRequest { [JsonProperty("names")] public IEnumerable<string> Names { get; set; } } |
I created a SayHelloOrchestrationTrigger class which contains my Durable Function using a OrchestrationTrigger
and a single step that by Durable Function will call using an ActivityTrigger
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
public static class SayHelloOrchestrationTrigger { [FunctionName(nameof(SayHelloOrchestrationTrigger))] public static async Task<List<string>> RunOrchestrator( [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger log, [Queue("sayhello-poison")] ICollector<string> errors) { log.LogInformation( $"{nameof(SayHelloOrchestrationTrigger)} trigger function processed a request."); var data = context.GetInput<SayHelloRequest>(); var outputs = new List<string>(); foreach (var name in data.Names) { try { var retryOptions = new RetryOptions(TimeSpan.FromSeconds(5), 3); outputs.Add( await context.CallActivityWithRetryAsync<string>( "Function1_Hello", retryOptions, name)); } catch { errors.Add( JsonConvert.SerializeObject( new SayHelloRequest { Names = new[] { name } })); } } return outputs; } [FunctionName("Function1_Hello")] public static string SayHello([ActivityTrigger] string name, ILogger log) { log.LogInformation( "Function1_Hello trigger function processed a request."); if (name == "Matt") { throw new ArgumentException(nameof(name)); } log.LogInformation($"Saying hello to {name}."); return $"Hello {name}!"; } } |
The RunOrchestrator method is looking for a SayHelloRequest class in the context
paramter. The method then loops through all the names
and calls the SayHello, which will output Hello {name}!
.
The retryOptions
variable defines the retry policy should an exception be encountered, in my example, I am defining the retry policy to try 3 times every 5 seconds.
If an exception is still encountered it then sends a new SayHelloRequest with the value of name
as the name that generated the error.
If the name Matt
is included in the list of names
and exception will be generated.
I then created two more functions, SayHelloHttpTrigger, which calls the Durable Function from an Http Request, and the other, SayHelloQueueTrigger, which looks for messages in the sayhello
Azure queue.
Run the Azure Function.
Open Postman, and execute the request.
Click on the statusQueryGetUri
to see the executed request.
Execute the request again and include Matt
.
Open Storage Explorer and look in the sayhello-poison
queue, you should see a new message with a names property consisting of one element called Matt
.
You can also trigger the Durable Function by dropping a message in the sayhello
queue.
That’s it! Pretty straight forward, though took some digging!
Related Articles
- https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-overview?tabs=csharp
- https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-bindings
- https://www.serverlessnotes.com/docs/retries-with-azure-durable-functions
Discover more from Matt Ruma
Subscribe to get the latest posts sent to your email.