0

According to official documents of the Azure Durable Functions, the fan-out/fan-in use case of it is shown in the example (from same site)

[FunctionName("FanOutFanIn")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    var parallelTasks = new List<Task<int>>();

    // Get a list of N work items to process in parallel.
    object[] workBatch = await context.CallActivityAsync<object[]>("F1", null);
    for (int i = 0; i < workBatch.Length; i++)
    {
        Task<int> task = context.CallActivityAsync<int>("F2", workBatch[i]);
        parallelTasks.Add(task);
    }

    await Task.WhenAll(parallelTasks);

    // Aggregate all N outputs and send the result to F3.
    int sum = parallelTasks.Sum(t => t.Result);
    await context.CallActivityAsync("F3", sum);
}

Functionally I can do the same thing with:

[FunctionName("FanOutFanIn")]
public static async Task Run(HttpTrigger... )
{
    var parallelTasks = new List<Task<int>>();

    // Get a list of N work items to process in parallel.
    object[] workBatch = await F1(null);
    for (int i = 0; i < workBatch.Length; i++)
    {
        Task<int> task = F2(workBatch[i]);
        parallelTasks.Add(task);
    }

    // custom error handling
    try {
        await Task.WhenAll(parallelTasks);
    } catch (Exception e){
        return new BadRequestObjectResult(e.ToString());
    }

    // Aggregate all N outputs and send the result to F3.
    int sum = parallelTasks.Sum(t => t.Result);
    bool success = await F3(sum);
    return success ? new OkObjectResult("SUCCESS") : new BadRequestObjectResult("ERROR");
}

public static async Task<object[]> F1(...){ ... }

What I want to ask is: What is the advantages of using Azure Durable Functions when you can implement it with not too much trouble with simple async/awaits?

Do people still prefer to use it while (comparing with using just async/awaits) it has disadvantages like: replaying of orchestrator fucntion is out of your control, orchestrator functions must use only deterministic stuff - plus the documents don't say how deterministic should it be (1)

I'm not implying there are few or no advantages, I just didn't find such a list on the interwebs.

Many people say the problem Durable Functions solve is the one about fanning in from multiple functions, but I think Durable Functions don't solve that, Task.WhenAll does, and Durable Functions just use it.

Note (1): I mean the level of deterministication F1 must be to be called in an orchestration function. Could it throw an Exception? Could it return a list of 5 objects sometimes and a list of 500 objects some other time? They even recommend you not use DateTime.Now which return the same type of object everytime. If I need the exact same thing everytime I would have defined a const.

Mr.K
  • 559
  • 4
  • 9

1 Answers1

1

Determinism in Durable is required for orchestrator functions because the code in the orchestrator function is executed again and again. When you await a durable task (like one returned when you invoke an activity), the orchestrator is suspended, and runs again from the beginning once the data is available from the activity. If you use DateTime.Now/Guid.NewGuid(), it'll be a different value on each execution, which can be bad, since code that was executed in a previous run should behave exactly the same as it did before. That's why things like database calls are done in activity functions. When the orchestrator awaits the task again on the next run, it'll receive the result and continue instead of suspending.

An activity function could return 5 items one time and 500 items another time. But the activity is only executed once, it's result is stored and used in runs that continue from that point.

A major difference in Durable with the fan-out/fan-in you describe is that in your regular function it all happens within one process. In Durable all of those activities are triggered in separate processes via a message queue. So it gives you much higher scale (I've killed an API by triggering thousands of activities at once that hit the API). Durable also gives you built-in retry for activity functions.

If you have a limited amount of these tasks and the only thing you do is run these in parallel, then a regular function can work fine. Durable might be overkill. I've found Durable really good when a process needs to wait for something to happen, e.g. a user clicking Approve in the app. Since you can also wait for external events, which suspends the orchestrator until the event arrives.

juunas
  • 54,244
  • 13
  • 113
  • 149
  • 2
    To add to this list, non-durable functions have a strict timeout limit. Durable functions let you orchestrate over much longer periods of time. – Stephen Cleary Jan 23 '20 at 13:54
  • 1
    Thanks @juunas for the informative answer, also I didn't expect to see a murder confession here (why did you kill that innocent API) – Mr.K Jan 24 '20 at 00:57