Azure / Azure/azure-functions-durable-powershell
Durable activity failures still not working properly in Fan out/Fan in orchestrator
- Dominant language
- C#
- Stars
- 9
- Forks
- 14
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 5
Description
Related item [https://github.com/Azure/azure-functions-powershell-worker/issues/748](url)
tl;dr: When using Fan out/fan in pattern, and one or more activity fails, the final output in the orchestrator function is dropped.
Still not able to handle activity errors in an orchestrator function when using the fan/out fan/in pattern. Although it seems to report the failure there is no way to get a working output for combinations of successful and failed activities at all. It either discards the successful output, or ignores the failure. Using same code to above issue but using the new SDK.
Trigger (Service Bus)
```powershell
param([string] $SbMsg, $TriggerMetadata)
$ErrorActionPreference = "Stop"
<#
Service bus message received:
{
"TestFilter": {
"version": "1.0.0",
"environment": "testEnv",
"itemName": [
"test1",
"test2",
"test3",
"test4",
"test5"
],
"appComponent": "testcomponent",
"application": "testApp"
}
}
#>
# Declare 5 cities to be randomly picked later
$city = @("London", "Paris", "New York", "Tokyo", "Sydney")
# Create a hashtable the orchestrator could use to iterate over
foreach ($item in $TriggerMetadata.TestFilter.itemName) {
$hashtableout += @{
$item = @{
city = $city | Get-Random
itemName = $item
}
}
}
# Start the orchestration
$InstanceId = Start-DurableOrchestration -FunctionName "of-test" -Input $hashtableout -ErrorAction Stop
Write-Host "Started orchestration with ID = '$InstanceId'"
```
Activity Function
```powershell
param($name)
$ErrorActionPreference = 'Stop'
$actStopwatch = [System.Diagnostics.Stopwatch]::StartNew()
if ($($name.itemName) -eq "test3") { throw }
elseif ($($name.itemName) -eq "test1") { Start-Sleep -Seconds 3}
elseif ($($name.itemName) -eq "test5") { Start-Sleep -Seconds 8 }
elseif ($($name.itemName) -eq "test4") { Start-Sleep -Seconds 50 }
# Finalise outputs
$actStopwatch.stop()
[decimal]$elapsedSecods = $actStopwatch.Elapsed.TotalMinutes
$actTotalTime = "{0:N2}" -f $elapsedSecods
Write-Information -MessageData "[$($name.itemName)] Completed in [$actTotalTime]..."
$name.add('runResults', "Deployment Succeeded. Runtime [$actTotalTime]")
"[$($name.itemName)] Completed in [$actTotalTime]"
```
So when using the below Orchestrator code everything works as expected regarding the error handling and getting final output from the orchestrator function. The orchestrator schedules the tasks and the final output includes any successful activity outputs as well as failures caught and handled within the orchestrator function. The problem with this orchestrator pattern however is the tasks are asynchronous and therefore do not run efficiently in that they queue behind each other (using sleep statements in the activity highlights this as can be seen below in the scheduled times)
Orchestrator (Working error handling, but pattern not optimal)
```powershell
using namespace System.Net
param($Context)
$ErrorActionPreference = 'Stop'
$output = @()
foreach ($property in $Context.Input.psobject.properties.name) {
# invoke the activity function
try {
$output += Invoke-DurableActivity -FunctionName "af-test" -Input $Context.Input.$property
}
catch {
$output += "[$($property)] Failed"
}
}
return $output
```

So the final output highlights the failure meaning the catch block works as expected BUT the scheduling of activities is done in series (function chaining) and therefore causes a completion time build up (last activity might run quickest but has to wait for all others to complete first)
Changing to the Fan out/fan in pattern, the behaviour changes:
```powershell
using namespace System.Net
param($Context)
$ErrorActionPreference = 'Stop'
$output = @()
$ParallelTasks =
foreach ($property in $Context.Input.psobject.properties.name) {
# invoke the activity function
try {
Invoke-DurableActivity -FunctionName "af-test" -Input $Context.Input.$property -NoWait
}
catch {
$output += "[$($property)] Failed"
}
}
$output += Wait-DurableTask -Task $ParallelTasks
return $output
```
And produces this error:

Changing to $ErrorActionPreference = 'Continue' allows the orchestrator to complete, but there is no output returned:

Moving the try/catch works partially in that it seems to capture the failures, but disregards the successful runs from the output:
```powershell
using namespace System.Net
param($Context)
$ErrorActionPreference = 'Stop'
$output = @()
$ParallelTasks =
foreach ($property in $Context.Input.psobject.properties.name) {
# invoke the activity function
Invoke-DurableActivity -FunctionName "af-test" -Input $Context.Input.$property -NoWait
}
try {
$output += Wait-DurableTask -Task $ParallelTasks
}
catch {
$output += "[$($property)] Failed"
}
return $output
```

It also does not collect multiple failures:

Using the same logic as in the fan out/fan in section here: [https://github.com/Azure/azure-functions-durable-powershell/blob/main/test/E2E/durableApp/DurablePatternsOrchestrator/run.ps1](url) .
```powershell
using namespace System.Net
param($Context)
$ErrorActionPreference = 'Continue'
$output = @()
$ParallelTasks = @()
foreach ($property in $Context.Input.psobject.properties.name) {
# invoke the activity function
try {
$ParallelTasks += Invoke-DurableActivity -FunctionName "af-test" -Input $Context.Input.$property -NoWait
}
catch {
$ParallelTasks += "[$($property)] Failed"
}
}
$output += Wait-DurableTask -Task $ParallelTasks
return $output
```
Again runs successfully, but does not handle the error or produce any final output. However, using this same orchestrator code but with no failing activities works and produces the final output:

So, to me it seems that whenever one or more activities fail, it just nukes any final output or captures only one.
I've tried relentlessly but cannot get the Fan out/fan in orchestrator to properly output when activities fail. I've also tried setting various different options for the $ErrorActionPreference to no avail. Would be great if error handling could work similar to when the chaining pattern is used and final output is generated with success or failures or both. Or perhaps there is a way to individually query the task results when using the Fan out/fan in pattern to achieve a similar result?
Contributor guide
Research direction
Start with test/E2E/durableApp/DurablePatternsOrchestrator/run.ps1 and reproduce the fan-out/fan-in case using the failing activity for test3. Compare its Wait-DurableTask behavior with the working chaining example and inspect how successful and failed activity results are collected. Done means the orchestrator returns all successful outputs and multiple failure results together.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure, powershell
- Domain
- backend, cloud
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100