When designing Azure functions one of the best practices that Microsoft recommends is Azure functions should be stateless.
But imagine you have this requirement, where you to need call an Azure function and the output of that function needs to be applied as the input of another function and so on and so forth.
|
Function Chaining |
This scenario is known as Function chaining. The very first function (say F0) which orchestrates F1, F2, F3, and F4 will need to maintain the return values and control the flow. But what if after x amount of time, the process running F0 recycles or the VM which F0 is running on rebooted. If we use regular Azure Functions, since we aren’t maintaining state, we can’t track at which point the F0 got failed.
Enter Durable Functions.
Durable Functions are designed to do all the hard work of maintaining the function state for us. It's built on top of
Durable Task Framework. As of today (10th April 2018) Durable Functions are in the preview stage.
In this post let's see how Durable Functions can be used to chain functions. It’s always best to go by an example. First, let’s see how we can create Durable Functions. Please note all the instructions given are as of today, these steps might change over time.
If you are using Azure Portal, if you create a new Function App, by default it’s using Runtime 1.x. And because of that, you won’t be able to see any Durable Function templates when creating new functions. For that, you need to go to Function App Settings and change the runtime to beta.
|
Function App Settings |
|
Runtime Version |
Here I have already switched to beta. Alternatively, you can go to Application Settings and change the FUNCTIONS_EXTENSION_VERSION to beta.
And then if you try to create a new function, you can see all the Durable Functions related templates.
|
Durable Functions Templates |
When you try to create a new function using one of the above templates, you will be prompted to install Durable Functions extension.
If you are using Visual Studio, make sure you are on 15.3 or greater and Azure Development workload is included in your setup.
And when you are creating new Function App, make sure to select Azure Functions V2 Preview (.NET Core).
|
New Project |
For this demo, let’s go ahead with Visual Studio. Once the project is created, let’s add a new function using Durable Functions Orchestration template.
|
Templates |
Once it’s created, you can see that Microsoft.Azure.WebJobs.Extensions.DurableTask NuGet package is added for us.
|
project.csproj |
I have modified the default file to something like below.
public static class Function1
{
[FunctionName("Function1")]
public static async Task<int> RunOrchestrator(
[OrchestrationTrigger] DurableOrchestrationContext context)
{
int x = await context.CallActivityAsync<int>("GetSum", new SumModel(1, 10));
int y = await context.CallActivityAsync<int>("GetSum", new SumModel(x, 10));
int z = await context.CallActivityAsync<int>("GetSum", new SumModel(y, 10));
return z; // z = 31
}
[FunctionName("GetSum")]
public static int GetSum([ActivityTrigger] SumModel model, TraceWriter log)
{
// Time consuming operation
return model.Number1 + model.Number2;
}
[FunctionName("Function1_HttpStart")]
public static async Task<HttpResponseMessage> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")]HttpRequestMessage req,
[OrchestrationClient]DurableOrchestrationClient starter,
TraceWriter log)
{
// Function input comes from the request content.
string instanceId = await starter.StartNewAsync("Function1", null);
log.Info($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
}
public class SumModel
{
public int Number1 { get; private set; }
public int Number2 { get; private set; }
public SumModel(int number1, int number2)
{
Number1 = number1;
Number2 = number2;
}
}
Here at the bottom, we have a HttpTrigger Function1_HttpStart to trigger the Orchestrator function which is Function1. Function1 will call GetSum multiple times to make it like a chain.
There are a couple of interesting things happening here. Imagine GetSum takes 10 seconds to complete. So that means Function1 will be running for more than 30 seconds. But it’s not. After calling GetSum, Function1 goes to sleep. And when GetSum returns completing his functionality, he notifies Function1 with the return values and Function1 resumes its operation. For all these time, the state is getting managed by the runtime.
Happy Coding.
Regards,
Jaliya