Azure Durable Function Patterns — External events

Cheranga Hatangala
Cheranga
Published in
4 min readAug 1, 2020

--

Introduction

Azure durable functions lets you implement stateful functions. This is especially important if you would like to orchestrate a particular set of functions such as in a work flow fashion. Most of the time Azure functions are designed and implemented as a micro-service but these services need to work together to achieve a particular requirement.

Azure durable functions have three main function types,

  • Client Function

This is the function which is in the front-line. This can be an HTTP API function, blob trigger, etc. This function’s responsibility is to get the input data and start a workflow.

  • Orchestrator function

As per the name, this is the function which will orchestrate several azure functions (activity functions) to achieve the end result. This is where the stateful nature is maintained. Orchestrator functions uses “event-sourcing” pattern to reliably execute functions in a stateful manner. Because of this there are some constraints which we need to follow especially when we implement orchestrator functions. Orchestrator functions have a trigger type called OrchestrationTrigger to differentiate them as an orchestrator.

  • Activity function.

This is where actually the action happens. This is a regular Azure function where you can implement the target micro-service. Activity functions have a trigger type called ActivityTrigger to identify them as activity functions.

Durable Function Patterns

You can implement some very important use cases using durable functions reliably, which might rather require quite complex design and implementation. Durable functions make it quite easy with their stateful nature and having some of the patterns already baked in.

External Event Pattern

This pattern is used if we would like to wait for some event to occur and then depending on the outcome of that particular event we would like to do certain actions. Usual characteristics of these scenarios are,

  • The operation is asynchronous.
  • The operation must wait for some event to occur.
  • The operation might allow a certain time limit for that event to occur. If the event does not occur in the given time it will take an action.
  • If the event do occur, depending on the status of it, there can be some other operations or a completely different workflow to trigger.

What are we developing?

We send an SMS (using Twilio) to a recipient and we would like to save it and update it’s status depending on the message sending was successful or not.

Sending SMS Workflow

The code for this solution can be found in GitHub.

Let’s explore the code.

Client Function

  • SendSmsClientFunction

This is an HTTP triggered Azure function. It accepts the SMS send request and starts the orchestration. Please note that any validations are not included to make it more concise.

Orchestrator Function

  • SendSmsOrchestratorFunction

This is the orchestration function. It will use several activity functions to achieve the feature we would like to implement.

  • Saves the recipient data (using the SaveSmsRecipientActivityFunctiion activity function).
  • Sends the SMS (using the SendSmsActivityFunction activity function) and waits for the external event to occur with a timeout( smssentevent ) at the same time.
  • It waits for the external event to occur by calling WaitForExternalEvent in the orchestration context. That’s all you need to do! Isn’t that awesome?!
  • From the race between two operations, if timeout wins it will use UpdateSmsRecipientActivityFunction activity function to update the table stating that the message could not be sent.
  • Else if the event triggered before the timeout it will use UpdateSmsRecipientActivityFunction activity function to update the table with the message status which was returned from the event data.

Activity Functions

  • SaveSmsRecipientActivityFunction

This will save the recipient data to the Azure storage table. Note that in here I have used Azure table storage bindings. But if you would like to separate the data access logic completely it’s better to implement a repository or a command/query pattern. But for the sake of brevity I have done the implementation inside the Azure function itself.

  • UpdateSmsRecipientActivityFunction

This Azure function will take appropriate action to update the recipient data depending on the message status. Again I’m not a big fan of mixing data access logic in here but this is done so that we can understand the code much easily.

  • SendSmsActivityFunction

We use Twilio to send an SMS. Using their API is quite easy but I did some changes to customize their HTTP Client so that it can be injected as a dependency and also we can have more control over the API calls made.

As you can see above there’s already an Azure Twilio binding (commented in above). You can use the binding if you would like to just send an SMS. But what if there are transient errors? and also what if you would like to get the response data from Twilio and do something about it? Hence we have implemented a custom HttpClient and a simple service called SendSmsService so that it can be injected as a dependency to the Azure function.

As you can see above the twilioclient has a custom HTTP header (it doesn’t do anything but to showcase that we can modify) and most importantly I have used Polly to include a retry policy to handle TooManyRequests (429) transient error as a demonstration.

Summary

As you can see we can implement this pattern using Azure durable function much more easily and reliably. If not for durable functions you’ll have to implement more components to achieve the same result. But this might be error prone.

Thank you.

--

--