“Hello Seoul!”[“Hello Seoul!”]
Orchestrator
Function
Activity
Function
Execution
History
var outputs = new List<string>();
outputs.Add(await context.CallActivityAsync<string>("SayHello", “Seoul"));
return outputs;
Orchestrator
Function
?
Activity
Function
“Hello Seoul!”
Orchestrator Started
Execution Started
Task Scheduled, SayHello, “Seoul”
Orchestrator Completed
Task Completed, “Hello Seoul!”
Orchestrator Started
Execution Completed, ["Hello Seoul!"]
Orchestrator Completed
[FunctionName(nameof(ActivityFn))]
public static string ActivityFn(
[ActivityTrigger] DurableActivityContext context, ILogger log)
{
string name = context.GetInput<string>();
string instanceId = context.InstanceId;
log.LogInformation($"[{instanceId}] Saying hello to {name}.");
return $"Hello {name}!";
}
{
"name": "<Name of input parameter in function signature>",
"activity": "<Optional - name of the activity>",
"type": "activityTrigger",
"direction": "in"
}
[FunctionName(nameof(OrchestratorFn))]
public static async Task<List<string>> OrchestratorFn(
[OrchestrationTrigger] DurableOrchestrationContext context)
{
var outputs = new List<string>();
outputs.Add(await context.CallActivityAsync<string>(nameof(ActivityFn), "Seoul"));
outputs.Add(await context.CallActivityAsync<string>(nameof(ActivityFn), "Busan"));
outputs.Add(await context.CallActivityAsync<string>(nameof(ActivityFn), "Incheon"));
return outputs;
}
{
"name": "<Name of input parameter in function signature>",
"orchestration": "<Optional - name of the orchestration>",
"type": "orchestrationTrigger",
"direction": "in"
}
[FunctionName(nameof(ClientFn))]
public static async Task<HttpResponseMessage> ClientFn(
[HttpTrigger(AuthorizationLevel.Anonymous, "get")]HttpRequestMessage req,
[OrchestrationClient]DurableOrchestrationClient starter,
ILogger log)
{
string instanceId = await starter.StartNewAsync(nameof(OrchestratorFn), null);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
{
"name": "<Name of input parameter in function signature>",
"taskHub": "<Optional - name of the task hub>",
"connectionName": "<Optional - name of the connection string app setting>",
"type": "orchestrationClient",
"direction": "in"
}
public class DurableOrchestrationClient : DurableOrchestrationClientBase
{
… Task<string> StartNewAsync(…);
… Task TerminateAsync(…);
… Task<IList<DurableOrchestrationStatus>> GetStatusAsync(…);
… Task TerminateAsync(…);
… HttpResponseMessage CreateCheckStatusResponse(…);
}
public class DurableOrchestrationStatus
{
public string Name { get; }
public string InstanceId { get; }
public DateTime CreatedTime { get; }
public DateTime LastUpdatedTime { get; }
public JToken Input { get; }
public JToken Output { get; }
public OrchestrationRuntimeStatus RuntimeStatus { get; }
public JToken CustomStatus { get; }
public JArray History { get; }
}
Pattern #1: Function Chaining
F1 F2 F3 F4
// calls functions in sequence
public static async Task<object> Run(DurableOrchestrationContext ctx)
{
try
{
var x = await ctx.CallFunctionAsync("F1");
var y = await ctx.CallFunctionAsync("F2", x);
var z = await ctx.CallFunctionAsync("F3", y);
return await ctx.CallFunctionAsync("F4", z);
}
catch (Exception)
{
// global error handling/compensation goes here
}
}
Pattern #2: Fan-Out/Fan-In
F1
F2
F3
public static async Task Run(DurableOrchestrationContext ctx)
{
var parallelTasks = new List<Task<int>>();
// get a list of N work items to process in parallel
object[] workBatch = await ctx.CallActivityAsync<object[]>("F1");
for (int i = 0; i < workBatch.Length; i++)
{
Task<int> task = ctx.CallActivityAsync<int>("F2", workBatch[i]);
parallelTasks.Add(task);
}
await Task.WhenAll(parallelTasks);
// aggregate all N outputs and send result to F3
int sum = parallelTasks.Sum(t => t.Result);
await ctx.CallActivityAsync("F3", sum);
}
Pattern #3: HTTP Async Response
Start DoWork
GetStatus
> curl -X POST https://myfunc.azurewebsites.net/orchestrators/DoWork -H "Content-Length: 0" -i
HTTP/1.1 202 Accepted
Location: https://myfunc.azurewebsites.net/orchestrators/b79baf67f717453ca9e86c5da21e03ec
Server: Microsoft-IIS/8.0
> curl https://myfunc.azurewebsites.net/orchestrators/b79baf67f717453ca9e86c5da21e03ec -i
HTTP/1.1 202 Accepted
Content-Length: 173
Content-Type: application/json
Location: https://myfunc.azurewebsites.net/orchestrators/b79baf67f717453ca9e86c5da21e03ec
Server: Microsoft-IIS/8.0
{"runtimeStatus":"Running","createdTime":"2017-03-16T21:20:36Z","lastUpdatedTime":"2017-03-
16T21:20:47Z"}
> curl https://myfunc.azurewebsites.net/orchestrators/b79baf67f717453ca9e86c5da21e03ec -i
HTTP/1.1 200 OK
Content-Length: 175
Content-Type: application/json
Server: Microsoft-IIS/8.0
{"runtimeStatus":"Completed","createdTime":"2017-03-16T21:20:36Z","lastUpdatedTime":"2017-03-
16T21:20:57Z"}
Worker
Worker
Worker
Worker
Worker
Stateful/Partitioned
Stateless
Control Queue(s)
Triggers orchestrator
function execution
Work Item Queue
Triggers activity function execution
History Table
Event Sourcing
Orchestrator code must be deterministic
Durable functions
Durable functions

Durable functions

  • 5.
    “Hello Seoul!”[“Hello Seoul!”] Orchestrator Function Activity Function Execution History varoutputs = new List<string>(); outputs.Add(await context.CallActivityAsync<string>("SayHello", “Seoul")); return outputs; Orchestrator Function ? Activity Function “Hello Seoul!” Orchestrator Started Execution Started Task Scheduled, SayHello, “Seoul” Orchestrator Completed Task Completed, “Hello Seoul!” Orchestrator Started Execution Completed, ["Hello Seoul!"] Orchestrator Completed
  • 7.
    [FunctionName(nameof(ActivityFn))] public static stringActivityFn( [ActivityTrigger] DurableActivityContext context, ILogger log) { string name = context.GetInput<string>(); string instanceId = context.InstanceId; log.LogInformation($"[{instanceId}] Saying hello to {name}."); return $"Hello {name}!"; } { "name": "<Name of input parameter in function signature>", "activity": "<Optional - name of the activity>", "type": "activityTrigger", "direction": "in" }
  • 9.
    [FunctionName(nameof(OrchestratorFn))] public static asyncTask<List<string>> OrchestratorFn( [OrchestrationTrigger] DurableOrchestrationContext context) { var outputs = new List<string>(); outputs.Add(await context.CallActivityAsync<string>(nameof(ActivityFn), "Seoul")); outputs.Add(await context.CallActivityAsync<string>(nameof(ActivityFn), "Busan")); outputs.Add(await context.CallActivityAsync<string>(nameof(ActivityFn), "Incheon")); return outputs; } { "name": "<Name of input parameter in function signature>", "orchestration": "<Optional - name of the orchestration>", "type": "orchestrationTrigger", "direction": "in" }
  • 11.
    [FunctionName(nameof(ClientFn))] public static asyncTask<HttpResponseMessage> ClientFn( [HttpTrigger(AuthorizationLevel.Anonymous, "get")]HttpRequestMessage req, [OrchestrationClient]DurableOrchestrationClient starter, ILogger log) { string instanceId = await starter.StartNewAsync(nameof(OrchestratorFn), null); log.LogInformation($"Started orchestration with ID = '{instanceId}'."); return starter.CreateCheckStatusResponse(req, instanceId); } { "name": "<Name of input parameter in function signature>", "taskHub": "<Optional - name of the task hub>", "connectionName": "<Optional - name of the connection string app setting>", "type": "orchestrationClient", "direction": "in" }
  • 12.
    public class DurableOrchestrationClient: DurableOrchestrationClientBase { … Task<string> StartNewAsync(…); … Task TerminateAsync(…); … Task<IList<DurableOrchestrationStatus>> GetStatusAsync(…); … Task TerminateAsync(…); … HttpResponseMessage CreateCheckStatusResponse(…); } public class DurableOrchestrationStatus { public string Name { get; } public string InstanceId { get; } public DateTime CreatedTime { get; } public DateTime LastUpdatedTime { get; } public JToken Input { get; } public JToken Output { get; } public OrchestrationRuntimeStatus RuntimeStatus { get; } public JToken CustomStatus { get; } public JArray History { get; } }
  • 14.
    Pattern #1: FunctionChaining F1 F2 F3 F4 // calls functions in sequence public static async Task<object> Run(DurableOrchestrationContext ctx) { try { var x = await ctx.CallFunctionAsync("F1"); var y = await ctx.CallFunctionAsync("F2", x); var z = await ctx.CallFunctionAsync("F3", y); return await ctx.CallFunctionAsync("F4", z); } catch (Exception) { // global error handling/compensation goes here } }
  • 15.
  • 16.
    public static asyncTask Run(DurableOrchestrationContext ctx) { var parallelTasks = new List<Task<int>>(); // get a list of N work items to process in parallel object[] workBatch = await ctx.CallActivityAsync<object[]>("F1"); for (int i = 0; i < workBatch.Length; i++) { Task<int> task = ctx.CallActivityAsync<int>("F2", workBatch[i]); parallelTasks.Add(task); } await Task.WhenAll(parallelTasks); // aggregate all N outputs and send result to F3 int sum = parallelTasks.Sum(t => t.Result); await ctx.CallActivityAsync("F3", sum); }
  • 17.
    Pattern #3: HTTPAsync Response Start DoWork GetStatus
  • 18.
    > curl -XPOST https://myfunc.azurewebsites.net/orchestrators/DoWork -H "Content-Length: 0" -i HTTP/1.1 202 Accepted Location: https://myfunc.azurewebsites.net/orchestrators/b79baf67f717453ca9e86c5da21e03ec Server: Microsoft-IIS/8.0 > curl https://myfunc.azurewebsites.net/orchestrators/b79baf67f717453ca9e86c5da21e03ec -i HTTP/1.1 202 Accepted Content-Length: 173 Content-Type: application/json Location: https://myfunc.azurewebsites.net/orchestrators/b79baf67f717453ca9e86c5da21e03ec Server: Microsoft-IIS/8.0 {"runtimeStatus":"Running","createdTime":"2017-03-16T21:20:36Z","lastUpdatedTime":"2017-03- 16T21:20:47Z"} > curl https://myfunc.azurewebsites.net/orchestrators/b79baf67f717453ca9e86c5da21e03ec -i HTTP/1.1 200 OK Content-Length: 175 Content-Type: application/json Server: Microsoft-IIS/8.0 {"runtimeStatus":"Completed","createdTime":"2017-03-16T21:20:36Z","lastUpdatedTime":"2017-03- 16T21:20:57Z"}
  • 19.
    Worker Worker Worker Worker Worker Stateful/Partitioned Stateless Control Queue(s) Triggers orchestrator functionexecution Work Item Queue Triggers activity function execution History Table
  • 21.
    Event Sourcing Orchestrator codemust be deterministic