Azure Function Apps and Durable Functions, Part 1: What Are Function Apps?

Daniel Ward
Mesh-AI Technology & Engineering
8 min readFeb 20, 2024

This is the first of a two part series covering Azure Function Apps. This part aims to cover the key components of what a Function App is, especially compared to its competitors. We don’t cover all intricacies, however, the second instalment covers more practical thoughts around working with Azure Function Apps and Durable Functions. If you’re:

  1. Not sure what value Serverless Functions add, start here.
  2. Eager to understand Durable Functions better, skip towards the end of this article.
  3. Looking for advice and to understand best practices for working with these services, skip to Part 2.

What are Azure Function Apps?

If you’ve built with AWS Lambda Functions or GCP Cloud Functions, you’ll already be familiar with the idea of Function Apps: They are short-running compute instances running specific code functions. They have a range of uses, but the driving factors are:

  1. Performing small, short-lived processes in an efficient manner.
  2. Providing low cost, highly available, and highly scalable processing.

They are a good alternative when a dedicated virtual machine or other compute instance would have large amounts of idle time and when you want to maximise parallel processing.

You are likely to see the term serverless appear when talking about Functions. In simple terms, this means that there is no dedicated compute for the code; when the code is required to run, the required compute is created, the code is run, then the infrastructure is immediately destroyed. Coincidentally, this is why these services are also all stateless, meaning they do not have any memory of what has previously been run (however, read on to understand why this is not a limiting factor for Function Apps and does not need to be for any serverless application).

How are Azure Function Apps Similar to AWS Lambdas and GCP Cloud Functions?

Infrastructure

AWS Lambdas and GCP Cloud Functions can only be deployed in a way that means the infrastructure is invoked when the function is called. This is also true of Azure Function Apps when deployed on a consumption plan.

A simplistic view of how Serverless Functions work. Note that “Function Backend” is not something described in the Azure Documentation (or AWS or GCP, as far as I’m aware), but is intended to help you understand what’s going on when you work with them.

In essence, when you deploy a function in a serverless configuration, your only standing resources are:

  1. Your code (and all its dependencies), waiting to run.
  2. One or multiple endpoints that will trigger your code.

When a trigger is hit, it creates the infrastructure, runs your code on it, then destroys the infrastructure again. The code ran may have parameters passed to it from the request that hits the trigger, but this is not required.

Triggers

All three services provide a range of ways to trigger your serverless code. In general, however, this boils down to one of two methods:

  1. Direct invocation via a HTTP(S) endpoint. In other words, you send a POST request to a HTTP(S) endpoint, which starts your code and may return a response.
  2. Invocation via an internal service event. This can be a wide range of items and varies by cloud platform, but common examples include files being added to a storage account, and timer or CRON events. In Azure, there is a wide range of triggers, covering Blob/Queue/Table Storage, CosmosDB, Event Grid, Event Hubs, and many more.

In all cases, this leads to some use-cases for event-driven architecture, where serverless processing becomes an obvious candidate.

How are Azure Function Apps Different to AWS Lambdas and GCP Cloud Functions?

Infrastructure

Unlike its competitors, Azure Function Apps can have dedicated compute. Whilst this sounds counter-intuitive, this doesn’t stop them being serverless. This can take a moment to wrap your head around, so let’s view it diagrammatically first:

A simplistic view of how an App Service Plan provides dedicated compute for Azure Function Apps. It’s worth noting that Function Apps ALWAYS have an App Service Plan, but that plan may have no dedicated compute, in which case it is a Consumption plan.

In short, your code is never deployed until it needs to run. When it does need to run, it’s spun up in a similar way to the serverless example, except the infrastructure for it to run on is already provisioned. This provides a few benefits, such as:

  1. Minimal to zero warm-up time*, as there is never a need to provision infrastructure before running code.
  2. Mostly fixed running costs, as your main expense will be the dedicated compute rather than the Function App invocations.
  3. Able to fence your service within your Virtual Network, removing public access.

✳️ *Warm-up time is the time to get your app ready to run when it’s requested. This can be several seconds for a consumption plan application.

However, it has a number of downsides too:

  1. Your costs are likely to be higher overall, virtually guaranteed if your app is run infrequently.
  2. You now are working within a maximum CPU and memory capacity, rather than (mostly) unlimited.

Statefulness

AWS Lambdas and GCP Cloud Functions do not have any native capacity for state, though approaches do exist for handling this for both AWS and for GCP. The catch is that these are workarounds. Fundamentally, if you want to manage state in these tools, the only option is to build a solution that will do that for you, such as submitting key information to a messaging queue or storage account with a unique identifier, ensuring that it can later be recalled. This comes with the need to clean up this information, implementing effective “time-out” functionality over your storage or message queue.

Meanwhile, Azure Function Apps have a capability called Durable Functions. This is an extension of Function Apps which allows your apps to maintain state entirely within the Function App infrastructure, without needing to develop your own method of maintaining state.

What are Durable Functions and how do they work?

⚠️ In my opinion, if you’re looking to get started with Durable Functions, understanding the underlying architecture is critical. The Azure documentation unfortunately glosses over how both Function Apps and Durable Functions actually work, instead diving straight into use cases. By understanding how it works, the use cases and how to apply them makes considerably more sense. The Azure documentation for Durable Functions needs improvement, so having and understanding of the underlying architecture helps a considerable amount.

Durable Functions are unique to Azure, although you could argue that AWS Step Functions perform a similar job (although, I would say they’re more like Azure Logic Apps). GCP does not have any similar functionality. As a result, we can’t easily compare to other clouds.

As mentioned above, Durable Functions allow you to maintain state within an Azure Function, such that a single app instance can continue to run beyond what would normally be possible purely within a serverless function. It does this by extending the packages used by languages supported by Function Apps to provide a representation of state.

Internally, Durable Functions work like the following:

This is, again, a simplistic (and generalised) view of what’s going on behind the scenes. You might find it helpful to know that the Function Backend is driven by a Messaging Queue system.

Ultimately, there are a few keys components:

  1. Trigger Functions: As in a regular Function App, you have triggers. These determine methods by which your Function App knows when to start, and may pass parameters on what to do next. Importantly for Durable Functions, triggers should only ever be used to start Orchestration Functions. Unlike with regular Function App, these are maintained as their own functions and run asynchronously, allowing them to trigger Orchestration Functions and then rapidly expire. This is important so that orchestration functions are able to persist.
  2. Orchestration Functions: Unlike a regular Function App, you now have a function which is used to maintain state. Typically, you will only have one of these for an application, and this is where state is maintained. Orchestration Functions can call on Activity Functions to run, which allows a single app to perform parallel processing. These functions run as long as the app is live.
  3. Activity Functions: These are simple apps, similar to a traditional Function App, except for that they are called by Orchestration Functions. They simply run end-to-end without any care for state.

In all cases, you’re likely to have at least one Trigger Function and a single Orchestration Function. For single-thread applications, Activity Functions are optional.

When would I use Durable Functions?

The Azure documentation covers this nicely in this article, with the ability to show the code in your Azure Functions language of choice. However, I want to cover the high-level main two reasons you would ever use them:

  1. You require external input part way through an application’s process. Having an Orchestration Function that can wait on a second trigger event to occur allows this.
  2. You need to run multiple processes simultaneously, but provide a single output at the end. Having an Orchestration Function that can wait on the response from across multiple Activity Functions allows this.

From the Azure documentation linked above, these two examples refer most closely to Use Case 5 (Human Interaction) and Use Case 2 (Fan out/Fan In) respectively. All other Use Cases are somehow an extension of the two reasons listed above, but do a poor job of explaining this. Fundamentally, if you keep in mind the above two reasons, you will know when Durable Functions are worth using.

Wrap-Up

This has been a very fast, high-level run-through of Azure Function Apps and Durable Functions. Hopefully, you now have a better understanding of the role Function Apps play and, in particular, how Durable Functions provide a unique capability versus their competitors.

In Part 2, we’ll be covering more technical aspects for working with Function Apps and, in particular, Durable Functions.

Afterthoughts

Useful for consideration is whether it’s worth your time to engage with Durable Functions. As we’ll discover in Part 2, having good knowledge upfront makes working with them considerably easier. For particularly tricky applications where Durable Functions may work, it might be easier just to create your own stateful serverless architecture.

In other words, you would create a messaging queue and Key-Value storage to have your function apps send messages/events to the message queue, and potentially store their state in a Key-Value storage. These messages trigger other Function Apps which either work using purely the payload in the message, or retrieve the previous function’s state from the Key-Value store.

This might seem weird to suggest at the end of an article talking about Durable Functions, but the simple reason why is that you will know exactly what your service is doing and can build in just the features you need. As I said earlier, the documentation for Durable Functions isn’t great and finding out that functionality exists can be half the battle. To save you the effort, and only if you are using Python, I strongly recommend you bookmark this link. While it’s still a pain to navigate, finding this link in the first place is surprisingly difficult.

While I hope Part 2 will help you avoid all the pitfalls I have experienced in working with Durable Functions, the reality is that building a system yourself is not that hard provided you give sufficient forethought to how you will identify states and how you will restore them from your Key-Value store.

--

--