Posted in:

It's only been a month since I released my Durable Functions Fundamentals course on Pluralsight, but it's great to see that the platform is continuing to evolve and pick up new features.

A new v1.5 was released yesterday, and so I thought I'd highlight some of my favourite updates since I released my course.

Durable Functions and Azure Functions v2

Azure Functions version 2 has been in development for some time, and is hopefully close to being ready to go live, but isn't quite finished yet, so for my course I created a sample app with Azure Functions v1 and then ported it to Azure Functions v2

The changes between the two were relatively minor, but one gotcha with Durable Functions and Azure Functions V2 revolves around using the CreateCheckStatusResponse method of DurableOrchestrationClient.

Here's my starter function that kicks off a new orchestration. Notice that it takes a HttpRequestMessage parameter and returns a HttpResponseMessage:

[FunctionName("ProcessVideoStarter")]
public static async Task<HttpResponseMessage> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]
    HttpRequestMessage req,
    [OrchestrationClient] DurableOrchestrationClient starter,
    TraceWriter log)
{
    string video = req.RequestUri.ParseQueryString()["video"];

    if (video == null)
    {
        return req.CreateResponse(HttpStatusCode.BadRequest,
            "Please pass the video location the query string");
    }

    log.Info($"About to start orchestration for {video}");

    var orchestrationId = await starter.StartNewAsync("O_ProcessVideo", video);

    return starter.CreateCheckStatusResponse(req, orchestrationId);
}

This compiles just fine in Azure Functions V2, but if you use the templates to create a new HTTP triggered function, then you'll end up with a function that takes a HttpRequest and returns an IActionResult. This prevents you from using the CreateCheckStatusResponse method in these new-style HTTP triggered functions.

The good news is that there is now a new CreateHttpManagementPayload method that generates a JSON payload containing the correct URIs for the management APIs for this orchestration that you can use for the response. So here's an updated version of my starter function that uses the new-style function signature.

[FunctionName("ProcessVideoStarter")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]
    HttpRequest req,
    [OrchestrationClient] DurableOrchestrationClient starter,
    TraceWriter log)
{
    string video = req.GetQueryParameterDictionary()["video"];

    if (video == null)
    {
        return new BadRequestObjectResult(
            "Please pass the video location the query string");
    }

    log.Info($"About to start orchestration for {video}");

    var orchestrationId = await starter.StartNewAsync("O_ProcessVideo", video);
    var payload = starter.CreateHttpManagementPayload(orchestrationId);
    return new OkObjectResult(payload);
}

Custom Orchestration Status

Another great recently added feature is the ability to store a custom orchestration status against any running orchestration. The orchestrator function can set this value by calling SetCustomStatus on the DurableOrchestrationContext.

When you use the REST API to query the status of a running orchestration, you get to see the value of this custom status, making it great for tracking how far through the workflow you are.

For example, you might just update it with a simple string just before calling an activity or sub-orchestration:

ctx.SetCustomStatus("sending approval request email");

This would result in the following when we queried the status for this instance:

{
  instanceId: "741b87080ed74430a17863d9ee437101",
  runtimeStatus: "Running",
  input: "example.mp4",
  customStatus: "sending approval request email",
  output: null,
  createdTime: "2018-06-22T10:54:50Z",
  lastUpdatedTime: "2018-06-22T10:54:58Z"
}

But you're not limited to strings. You can store any serializable object in there, so it could include the IDs of sub-orchestrations that have been kicked off, or URLs of files that have been created in blob storage or IDs of rows that have been added to a database. It's a simple feature, but very powerful if you want to make your workflows easier to diagnose.

Enumerating Instances

Another great new feature in 1.5, is that now we can call an API (or use the GetStatusAsync method on the DurableOrchestrationClient) to retrieve details of all orchestrations stored in the task hub. This allows you to discover the orchestration ids of any running orchestrations, if you'd failed to keep track of them.

I think that management APIs like this will be vital in persuading developers to be willing to give Durable Functions a go in production. However, I think it needs to go further and give us support for paging and filtering the orchestrations (as there could be a vast number of them if Durable Functions is being used heavily), and it would also be good to be able to purge the event sourcing history of old Durable Functions from the task hub, either to simply clean up and save space, or as part of a required data retention cleanup.

I've submitted a GitHub issue with my desired enhancements to this feature, so do get involved in the discussion if you think this is something that would be useful to you as well.

JavaScript support

Finally, I have to mention that it seems like great progress is being made on adding JavaScript support to Durable Functions. It's not a feature I've tried myself yet, but there are some sample apps which give you a good feel for the required syntax in a JavaScript orchestrator function. Hopefully supporting more languages will result in Durable Functions being embraced by a wider pool of developers.

Want to learn more about how easy it is to get up and running with Durable Functions? Be sure to check out my Pluralsight course Azure Durable Functions Fundamentals.