Related item https://github.com/Azure/azure-functions-powershell-worker/issues/748
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)
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
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)
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:
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:
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 .
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?