What is function chaining in Azure Durable Functions?

Before understanding function chaining, we need to know what Azure Durable Functions are and the benefits of this framework.

What are Durable Functions?

In a Serverless architecture, developers are able to create a short-lived, stateless function that has one responsibility.

The function’s responsibility could be processing a file upload, acting as an API endpoint for a specific task, etc.

While this is great, there are some drawbacks. For example what if there were a situation where you want to write functions that are not short-lived or stateless? This is where durable functions come in.

Durable Functions is an open-source framework for Azure Functions that allows developers to write long-running orchestrations as a single function while maintaining state for all function calls.

The functions chaining pattern

In function chaining, there is an ordered list of functions that execute in a particular order, where the output of one function is the input of another function.

Let’s look at the diagram below:

widget

Here, there are three functions, UploadImage, TransformToPdf, and SavePdfToStorage, that following each other. Let’s imagine we want to call these functions when a post request is made to our application.

There are three key components that make this possible:

  1. An orchestrator function (called ImageToPdfOrchestrator). This orchestrator calls UploadImage, TransformToPdf, and SavePdfToStorage in the order we want, while maintaining state.

  2. Our three activity functions, UploadImage, TransformToPdf, and SavePdfToStorage, each perform their own individual task.

  3. An HTTP triggered durable client functioncalled ImageToPdf that starts an instance of the orchestrator.

Client functions

You can start an instance of an orchestrator function using a client function. In this example, our client function is an HTTP triggered function called ImageToPdf. To start instances of ImageToPdfOrchestrator, we will call the HTTP triggered function.

To interact with the orchestrators, the function must include a DurableClient input binding.

public static class ImageToPdf
{
[FunctionName("ImageToPdf")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "imagetopdf")] HttpRequestMessage req,
[DurableClient] IDurableClient starter,
ILogger log)
{
var instanceId = Guid.NewGuid().ToString();
var request = await req.Content.ReadAsAsync<RequestObject>();
await starter.StartNewAsync("ImageToPdfOrchestrator", instanceId, request.LocalImageLink);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
}
public class RequestObject
{
public string LocalImageLink { get; set; }
}

Orchestrators

In orchestrator functions, there must be a DurableOrchestrationContext parameter. The aim of the DurableOrchestrationContext is to call multiple activity functions using its CallActivityAsync method.

The ImageToPdfOrchestrator orchestrator function below first calls the UploadImage function, and then returns a link to the image on the cloud.

Next, the orchestrator calls the TransformToPdf function. After execution, a temporary link to the recently created pdf file is returned.

Thirdly, the orchestrator calls the SavePdfToStorage function. After execution, a link to the uploaded pdf document is returned.

[FunctionName("ImageToPdfOrchestrator")]
public static async Task<List<string>> Run(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
// Get the string input
var localImageLink = context.GetInput<string>();
// This is where the activity functions are chained
// 1. Call UploadImage to upload a local image to the cloud and return the online link
var cloudImageLink = await context.CallActivityAsync<string>("UploadImage", localImageLink);
// 2. Call TransformToPdf to transform the uploaded image to a pdf document
var temporaryPdfLink = await context.CallActivityAsync<string>("TransformToPdf", cloudImageLink);
// 3. Call SavePdfToStorage to upload the pdf document to a storage account
var cloudPdfLink = await context.CallActivityAsync<string>("SavePdfToStorage", temporaryPdfLink);
return cloudPdfLink;
}

Activities

Activities use the [ActivityTrigger] attribute. If you’d like to perform actions like GetInput<T> (similar to what we did in the orchestrator on line 6), use the IDurableActivityContext.

In our example, we have three activities: UploadImage, TransformToPdf, and SavePdfToStorage.

[FunctionName("UploadImage")]
public static string UploadImage([ActivityTrigger] IDurableActivityContext context)
{
string link = context.GetInput<string>();
// code to upload image
// ...
return imageLink;
}
[FunctionName("TransformToPdf")]
public static string TransformToPdf([ActivityTrigger] IDurableActivityContext context)
{
string link = context.GetInput<string>();
// code to transform pdf
// ...
return pdfLink;
}
[FunctionName("SavePdfToStorage")]
public static string SavePdfToStorage([ActivityTrigger] IDurableActivityContext context)
{
string link = context.GetInput<string>();
// code to save pdf to storage
// ...
return pdfStorageLink;
}

As we have seen in this shot, durable functions allow us to chain multiple functions while maintaining state. This can be useful in multiple scenarios, including eCommerce, where there may be separate functions for checkout, charging the card, and logging the order.