Skip to content

Writing Task Orchestrations

Chris Gillum edited this page Jul 2, 2020 · 2 revisions

Task orchestrations basically invoke Task Activities and define how the control flows from one activity to another. The code that can be written within an orchestration is plain C# but with a few constraints. These constraints exist because of how the framework replays the orchestration code. This is described in a nutshell below.

Every time new work needs to be processed by an orchestration (e.g. a Task Activity finished or a timer fired), the framework replays the user’s TaskOrchestration code from scratch. Whenever this user code attempts to schedule a TaskActivity, the framework intercepts this call and consults the ‘execution history’ of the orchestration. If it finds that the particular TaskActivity had already been executed and yielded some result, it would replay that Activity’s result immediately and the TaskOrchestration would continue. This would continue happening until the user code has executed to a point where either it is finished or it has scheduled a new Activity. If it is the latter case then the framework would actually schedule and execute the specified Activity. After this Activity is completed its result also becomes part of the execution history and the value would be used in subsequent replays.

With this in mind here are the constraints of the type of code we can write within a TaskOrchestration:

  • Code must be deterministic since it is going to be replayed multiple times, it must yield the same result every time. E.g. there cannot be any direct calls to get the current date/time, random numbers, Guids or remote service invocations etc.
  • There is a helper API on the OrchestrationContext object passed to the TaskOrchestration.RunTask() method that provides a deterministic way to get the current date/time. This should be used instead of System.DateTime.
  • Users can make the non-deterministic operations deterministic by wrapping them within TaskActivities. E.g. GenerateGuidActivity, GenerateRandomNumberActivity etc. Since Task Activity results are replayed by the framework, the non-deterministic value will be generated once on first execution and then on subsequent executions the same value will be replayed.
  • In the future, other helper APIs will also be added to the OrchestrationContext.
  • Code should be non-blocking i.e. no thread sleep or Task.WaitXXX() methods. The framework provides helper methods to setup async timers which should be used instead.
  • The execution history of an orchestration has a record of all scheduled task activities and their results. This history is also bounded by size limitations of Service Bus (when using the Service Bus provider) so as a consequence, infinite loops are not possible without user-aided checkpointing (described in Eternal Orchestrations).

The Task Orchestration code is always executed in a single thread. This means that if the code was awaiting multiple tasks and one of them completed followed immediately by another one, the framework is guaranteed to run the continuations for both of these tasks serially.