Traditional functions have below traits:
As good as it sounds, there are couple of disadvantages to how traditional functions works.
With durable function, we can overcome these disadvantages by maintaining local state and orchestrating multiple functions using orchestrator.
A tiny example of what it can do. We can effectively paralelise the work. If we are generating a report, for each order, I can call the shipping api but this each call to the shipping API would take some time
Now let’s have a look at the components of the durable functions!
Starter function
Starts orchestration or triggers the process (e.g. report, create an order) and sends the message to the orchestrator. Often a http endpoint.
Orchestrator function
This function coordinates activities and is the heart of the durable function. It also does status management.
Activity function
This is the function that will actually perform the work.
Firstly, starter function gets the request and calls the orchestrator function. Then, execution history tracks the history in the history table. e.g. 1. Orchestrator started, 2. Execution started.
Instead of calling activity function directly, it goes to execution history log and asks, ‘hey, did we peform this activity function?‘. If not, orchestrator function starts the activity function. This also gets recorded in the history table and then, the orchestration completes. Our activity function wakes up and then looks at the history table and finds that it has to run the function. After activity function finishes running, it updates the execution history. e.g. Task completed
One of the cool thing about durable function is that it remembers the state. When the orchestration function gets called again the second time, it asks the execution history, ‘Have I run the activity function?’ and the execution history will say ‘Yes’ and return the output from the first call.
Replayability means that the orchestrator code must be deterministic. What that means is that the orchestrator function will be replayed multiple times but it must produce the same result each time.
For example, if you use Guid.NewGuid
everytime the orchestrator function runs, it’s going to generate differrent result each time. Use something like DurableOrchestrationContext.CurrentUtcTime
You can also use activity function in order to keep orchestrator function deterministic. e.g. Do I/O in activity function.
One last thing, don’t write infinite loop! As we’ve learnt from above, , history logs will record everything and the inifite loops will cause memory to run out. Use DurableOrchestrationContext.ContinueAsNew
to clean all the logs.