Skip to content

Error Handling & Compensation

Andrey Zaytsev edited this page Aug 4, 2022 · 2 revisions

Any exception thrown in the TaskActivity code is marshaled back and thrown as a TaskFailedException in the TaskOrchestration code. Users can write the appropriate error handling and compensation code that suits their needs around this.

public class DebitCreditOrchestration : 
    TaskOrchestration<object, DebitCreditOperation>
{
    public override async Task<object> RunTask(OrchestrationContext context, 
        DebitCreditOperation operation)
    {
        bool failed = false;
        bool debited = false;
        try
        {
            await context.ScheduleTask<object>(typeof (DebitAccount),
                            new Tuple<string, float>(operation.SourceAccount, operation.Amount));
            debited = true;

            await context.ScheduleTask<object>(typeof(CreditAccount),
                            new Tuple<string, float>(operation.TargetAccount, operation.Amount));
        }
        catch (TaskFailedException exception)
        {
            if (debited)
            {
                // can build a try-catch around this as well, in which case the 
                // orchestration may either retry a few times or log the inconsistency for review
                await context.ScheduleTask<object>(typeof(CreditAccount),
                            new Tuple<string, float>(operation.SourceAccount, operation.Amount));
            }
        }

        return null;
    }
}

Note that pre-dotnet 6.0, the await keyword cannot be used within a catch block due to a CLR limitation. In that case, we can modify the snipped above to set up a failed flag and perform a compensation before returning a result:

# ... orchestration code here
        catch (TaskFailedException exception)
        {
            failed = true;
        }

        if (failed)
        {
            if (debited)
            {
                // can build a try-catch around this as well, in which case the 
                // orchestration may either retry a few times or log the inconsistency for review
                await context.ScheduleTask<object>(typeof(CreditAccount),
                            new Tuple<string, float>(operation.SourceAccount, operation.Amount));
            }
        }

        return null;
    }
}