Durable Functions for workflows in Azure - Time & Space Complexity
Start learning this pattern below
Jump into concepts and practice - no test required
When using Durable Functions for workflows, it's important to understand how the number of steps affects the time it takes to complete the process.
We want to know how the workflow's execution time grows as we add more tasks.
Analyze the time complexity of this Durable Function orchestration.
[FunctionName("Orchestrator")]
public static async Task<string[]> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var tasks = new List<Task<string>>();
var inputs = context.GetInput<List<string>>();
foreach (var input in inputs)
{
tasks.Add(context.CallActivityAsync<string>("ActivityFunction", input));
}
var results = await Task.WhenAll(tasks);
return results;
}
This orchestration calls an activity function for each input item in parallel and waits for all to complete.
Look at what repeats as input grows:
- Primary operation: Calling the activity function for each input item.
- How many times: Once per input item, so as many times as the number of inputs.
Each new input adds one activity call that runs in parallel.
| Input Size (n) | Approx. API Calls/Operations |
|---|---|
| 10 | 10 activity calls |
| 100 | 100 activity calls |
| 1000 | 1000 activity calls |
Pattern observation: The number of calls grows directly with the number of inputs.
Time Complexity: O(n)
This means the total number of activity calls grows linearly as you add more inputs.
[X] Wrong: "Because activities run in parallel, adding more inputs won't increase total execution time."
[OK] Correct: While activities run at the same time, the orchestration still needs to start each one, so the number of calls grows with inputs, affecting resource use and orchestration overhead.
Understanding how workflows scale with input size shows you can design cloud processes that handle growth smoothly and predict resource needs well.
"What if the orchestration called activities one after another instead of in parallel? How would the time complexity change?"
Practice
Solution
Step 1: Understand Durable Functions role
Durable Functions are designed to handle workflows that take a long time and need to be reliable.Step 2: Compare options
Options A, B, and D describe other Azure services, not Durable Functions.Final Answer:
To manage long-running workflows reliably -> Option DQuick Check:
Durable Functions = Manage workflows reliably [OK]
- Confusing Durable Functions with storage services
- Thinking Durable Functions create VMs
- Assuming Durable Functions monitor networks
Solution
Step 1: Identify orchestrator function syntax
Orchestrator functions must be async to support awaiting activity calls.Step 2: Differentiate activity and orchestrator functions
Options C and D define activity functions, not orchestrators.Final Answer:
async function orchestrator(context) { /* workflow code */ } -> Option CQuick Check:
Orchestrator = async function [OK]
- Using non-async function for orchestrator
- Confusing activity function syntax with orchestrator
- Omitting async keyword
const result = yield context.callActivity('SayHello');
return result + ' World!';Solution
Step 1: Understand callActivity result
The activity function returns 'Hello', which is assigned to result.Step 2: Concatenate strings
The orchestrator returns result + ' World!', so 'Hello' + ' World!' = 'Hello World!'.Final Answer:
"Hello World!" -> Option AQuick Check:
Result + ' World!' = 'Hello World!' [OK]
- Reversing string order
- Returning only activity result without addition
- Ignoring yield keyword effect
async function orchestrator(context) {
const result = context.callActivity('Task');
return result;
}Solution
Step 1: Check callActivity usage
callActivity returns a promise and must be awaited or yielded inside orchestrator.Step 2: Identify missing await/yield
The code calls callActivity without await or yield, causing incorrect behavior.Final Answer:
Missing await or yield before callActivity -> Option BQuick Check:
callActivity needs await/yield [OK]
- Forgetting await/yield on callActivity
- Thinking async keyword is wrong here
- Misplacing callActivity outside orchestrator
Solution
Step 1: Understand sequential calls in orchestrator
Each callActivity must be awaited to get the actual result before next call.Step 2: Check options for proper await usage
Only const result1 = await context.callActivity('Activity1'); const result2 = await context.callActivity('Activity2'); return result1 + ' & ' + result2; awaits both calls, ensuring correct sequence and result combination.Final Answer:
const result1 = await context.callActivity('Activity1');\nconst result2 = await context.callActivity('Activity2');\nreturn result1 + ' & ' + result2; -> Option AQuick Check:
Await both activities for correct sequence [OK]
- Not awaiting callActivity causing promises instead of results
- Mixing awaited and non-awaited calls
- Returning promises instead of strings
