If you’re looking to build complex workflows in a serverless environment, Azure Durable Functions may be the perfect solution for you. With Durable Functions, you can write long-running workflows that span multiple function invocations and even multiple instances of your functions.
In this post, we’ll take a closer look at Durable Functions and how to use them to orchestrate complex workflows with an example of function chaining
What are Azure Durable Functions?
Azure Durable Functions is a serverless computing platform that allows you to build long-running workflows in a serverless environment. Durable Functions allow you to develop event-triggered functions that execute a sequence of steps to achieve a specific objective.
One of the main advantages of Durable Functions is that they allow you to write stateful functions that can maintain their state across multiple invocations. This makes it possible to write workflows that span multiple function invocations and even multiple instances of your functions.
Demo
Function Chaining with Azure Durable Functions
One of the most common use cases for Azure Durable Functions is function chaining, which involves executing a sequence of functions in a specific order, with the output of one function serving as the input for the next function.
Here’s an example of function chaining with Durable Functions:
[FunctionName("Chaining")]
public static async Task<string> Run(
[OrchestrationTrigger] IDurableOrchestrationContext context,
ILogger log)
{
try
{
// Call Activity Function 1
string input1 = null; // Replace with your input
string result1 = await context.CallActivityAsync<string>("GetInput", input1);
// Call Activity Function 2 with the result of Activity Function 1
string input2 = result1; // Replace with your input
string result2 = await context.CallActivityAsync<string>("ProcessInput", input2);
// Call Activity Function 3 with the result of Activity Function 2
string input3 = result2; // Replace with your input
string result3 = await context.CallActivityAsync<string>("TransformData", input3);
// Call Activity Function 4 with the result of Activity Function 3
string input4 = result3; // Replace with your input
string finalResult = await context.CallActivityAsync<string>("PersistData", input4);
return finalResult;
}
catch (Exception ex)
{
// Error handling or compensation goes here.
log.LogError(ex, "An error occurred in the orchestration.");
throw;
}
}
[FunctionName("GetInput")]
public static string GetInput([ActivityTrigger] string input, ILogger log)
{
log.LogInformation($"Executing GetInput with input: {input}");
// Do some work
return "Result of GetInput";
}
[FunctionName("ProcessInput")]
public static string ProcessInput([ActivityTrigger] string input, ILogger log)
{
log.LogInformation($"Executing ProcessInput with input: {input}");
// Do some work with input
return "Result of ProcessInput";
}
[FunctionName("TransformData")]
public static string TransformData([ActivityTrigger] string input, ILogger log)
{
log.LogInformation($"Executing TransformData with input: {input}");
// Do some work with input
return "Result of TransformData";
}
[FunctionName("PersistData")]
public static string PersistData([ActivityTrigger] string input, ILogger log)
{
log.LogInformation($"Executing PersistData with input: {input}");
// Do some work with input
return "Result of PersistData";
}
In this example, the Chaining
function uses an IDurableOrchestrationContext
to orchestrate a sequence of activities. The function starts by calling GetInput
, then passes the output of
to GetInput
ProcessInput
, and so on, until it reaches the final activity PersistData
Starting the Orchestration with an HTTP Trigger
To start the Chaining
orchestration, you can create an HTTP trigger function that uses the IDurableOrchestrationClient
to start the orchestration:
[FunctionName("HttpStart")]
public static async Task<IActionResult> HttpStart(
[HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
[DurableClient] IDurableOrchestrationClient starter,
ILogger log)
{
string instanceId = await starter.StartNewAsync("Chaining", null);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
In this example, the HttpStart
function is triggered by an HTTP POST request and uses the IDurableOrchestrationClient
to start the Chaining
orchestration. Subsequently, the function responds with a URL that can be used to check the status.
Monitoring the Status of Orchestrations
When the StartOrchestrator
function returns, it includes a URL that can be used to check the status of the orchestration. You can use this URL to keep track of the orchestration’s advancement, and you can also transfer it to other systems for additional processing.
return starter.CreateCheckStatusResponse(req, instanceId);
Upon accessing this URL, the Durable Functions framework will provide a response that includes the current status of the orchestration. The response is structured as a JSON object that encompasses information regarding the current state, as well as any completed and pending actions.
For example, here’s the response for the Chaining
orchestration when it’s in progress:
{
"name": "Chaining",
"instanceId": "40a6d92f7c50427c8dbb6c46f6de86b7",
"runtimeStatus": "Running",
"input": null,
"output": null,
"createdTime": "2022-05-01T00:00:00Z",
"lastUpdatedTime": "2022-05-01T00:01:00Z",
"historyEvents": [
{
"eventId": 0,
"eventTime": "2022-05-01T00:00:00Z",
"eventType": "ExecutionStarted",
"functionName": "Chaining",
"orchestrationInstance": {
"instanceId": "40a6d92f7c50427c8dbb6c46f6de86b7",
"executionId": null
},
"input": null,
"output": null
},
{
"eventId": 1,
"eventTime": "2022-05-01T00:00:00Z",
"eventType": "FunctionScheduled",
"functionName": "F1",
"isPlayed": true,
"input": null,
"output": null
},
{
"eventId": 2,
"eventTime": "2022-05-01T00:00:05Z",
"eventType": "FunctionCompleted",
"functionName": "F1",
"isPlayed": true,
"input": null,
"output": "F1 output"
},
{
"eventId": 3,
"eventTime": "2022-05-01T00:00:10Z",
"eventType": "FunctionScheduled",
"functionName": "F2",
"isPlayed": true,
"input": "F1 output",
"output": null
}
]
}
As you can see, the response includes information about the Chaining
orchestration, including the instance ID, the runtime status, and the input and output values. It also includes a list of history events that show the progress of the orchestration.
By monitoring the status of the orchestration, you can observe which functions called, when they called, and what input and output values they received. Observing the status of the orchestration can aid in debugging and troubleshooting, and it can also facilitate the creation of dashboards and other monitoring tools.
How to invoke this function?
you can invoke the HttpStart
function with an HTTP POST request, and it will automatically trigger the Chaining
function.
To invoke the HttpStart
function, you can use an HTTP client or a tool like curl
. Here’s an example curl
the command that sends an HTTP POST request to the URL of the HttpStart
function:
curl -X POST https://<function-app-name>.azurewebsites.net/api/HttpStart?code=<function-key>
Replace <function-app-name>
with the name of your Azure Functions app, and <function-key>
with the function key for the HttpStart
function (which you can find in the Azure portal).
When you send the POST request, Azure Functions will invoke the HttpStart
function, which will start the Chaining
orchestration. The Chaining
function will then execute the specified sequence of activities.
How to run from Visual Studio?
If you run this example in Visual Studio, you can use the Azure Functions Core Tools to run and test the functions locally. Once you have started the Functions runtime locally, you can use an HTTP client such as Postman or cURL to invoke the HttpStart function by sending an HTTP POST request to the endpoint provided by the Azure Functions Core Tools.
To invoke the HttpStart function from a client, you would send an HTTP POST request to the URL provided by the Azure Functions Core Tools, with the input payload in the request body. For example, if the URL provided by the Core Tools is http://localhost:7071/api/HttpStart
, and the input payload is a JSON object with a name
property, the request might look like this:
POST http://localhost:7071/api/HttpStart
Content-Type: application/json
{
"name": "John"
}
This will trigger the HttpStart function, which will then invoke the Chaining function to orchestrate the workflow. You can use the Azure Functions Core Tools to view the output of each function in the workflow, as well as any logs or error messages generated by the functions.
Application Source Code @ LearnSmartCoding GitHub
Check out other topics that might interest you.
Conclusion
Durable Functions provide a powerful way to build complex workflows and orchestrations using serverless technology. By using the Durable Functions framework, you can easily build long-running, stateful functions that can handle complex business processes and workflows.
In this article,
We demonstrated how Durable Functions can implement function chaining by passing the output of one function as input to another function in a series of dependent steps. We started with an HTTP trigger that initiated the workflow and used an orchestrator function to chain together a series of activity functions.
By breaking down a complex task into smaller, more manageable functions, and using Durable Functions to orchestrate them, we can create efficient, scalable workflows that can handle large volumes of data and complex dependencies.
In general, Durable Functions provide developers with the capability to design intricate workflows in a serverless environment and are optimal for a diverse range of applications such as data processing and event-driven automation.