July 2020: Hold my state - Creating Workflows with Azure Durable Functions by Maxime Rouiller

July 2020: Hold my state - Creating Workflows with Azure Durable Functions by Maxime Rouiller

Check out the video to this session here:
https://youtu.be/KkxudCUYB30

Do your functions have asynchronous dependencies or long running requirements? Fan out and fan in? Jumping through hoops or creating additional overhead trying to maintain state? Take a load off, Durable Functions has your back. Come learn how to simplify complex workflows and win your weekends back.

Bio:
I've been in the tech industry since the 2000s. I've always been working with .NET for as far as I can remember. I've built my profile on being a master of all trade. I've setup VMs, SharePoint environments, built servers and what not. I've coded from desktop apps to server apps, but I've truly found my passion around the web, however. Since then, I've been consulting independently for 3 years before seeing an opportunity to join Microsoft. I co-manage a local user group in Montreal, I own my own blog.

If you want to talk bread and bicycling, I'm your man.

You can find him at
https://blog.maximerouiller.com/
https://developer.microsoft.com/en-us/advocates/maxime-rouiller

0754d30f3acc99a940aebdcd49d5af97?s=128

Azure Zurich User Group

July 09, 2020
Tweet

Transcript

  1. None
  2. Author functions in C#, F#, Node.JS, Java, and more Code

    Events React to timers, HTTP, or events from your favorite Azure services, with more on the way Outputs Send results to an ever- growing collection of services
  3. Language 1.x 2.x 3.x C# GA (.NET Framework 4.7) GA

    (.NET Core 2.2) GA (.NET Core 3.1) JavaScript GA (Node 6) GA (Node 8 & 10) GA (Node 10 & 12) F# GA (.NET Framework 4.7) GA (.NET Core 2.2) GA (.NET Core 3.1) Java N/A GA (Java 8) GA (Java 8) PowerShell N/A GA (PowerShell Core 6) GA (PowerShell Core 6) Python N/A GA (Python 3.6 & 3.7) GA (Python 3.6, 3.7, & 3.8) TypeScript N/A GA1 GA1 https://docs.microsoft.com/azure/azure-functions/supported-languages
  4. None
  5. None
  6. None
  7. public static string SayHello(string name) { return $"Hello {name}!"; }

  8. // calls functions in sequence public static async Task<List<string>> Run(DurableOrchestrationContext

    context) { var outputs = new List<string>(); outputs.Add(await context.CallActivityAsync<string>("SayHello", “Seattle")); outputs.Add(await context.CallActivityAsync<string>("SayHello", “Amsterdam")); // returns ["Hello Seattle!", "Hello Amsterdam!"] return outputs; }
  9. module.exports = async function(context) { return `Hello ${context.bindings.name}!`; };

  10. // calls functions in sequence module.exports = df.orchestrator(function*(context){ const output

    = []; output.push(yield context.df.callActivity("E1_SayHello", "Seattle")); output.push(yield context.df.callActivity("E1_SayHello", "Amsterdam")); // returns ["Hello Seattle!", "Hello Amsterdam!"] return output; });
  11. def main(name: str) -> str: return f"Hello {name}"

  12. # calls functions in sequence def orchestrator_function(context: df.DurableOrchestrationContext): task1 =

    yield context.call_activity("SayHello", "Seattle") task2 = yield context.call_activity("SayHello", "Amsterdam") outputs = [task1, task2] # returns ["Hello Seattle!", "Hello Amsterdam!"] return outputs main = df.Orchestrator.create(orchestrator_function)
  13. None
  14. “Hello Amsterdam!” [“Hello Amsterdam!”] Orchestrator Function Activity Function Execution History

    var outputs = new List<string>(); outputs.Add(await context.CallActivityAsync<string>("SayHello", “Amsterdam")); return outputs; Orchestrator Function ? Activity Function “Hello Amsterdam!” Orchestrator Started Execution Started Task Scheduled, SayHello, “Amsterdam” Orchestrator Completed Task Completed, “Hello Amsterdam!” Orchestrator Started Execution Completed, ["Hello Amsterdam!"] Orchestrator Completed
  15. 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 } }
  16. F1 F2 F3

  17. public static async Task DurableOrchestrationContext var new int // get

    a list of N work items to process in parallel object await object "F1" for int Task int int "F2" await Task // aggregate all N outputs and send result to F3 int await "F3"
  18. None
  19. public static async Task Run(DurableOrchestrationContext ctx) { await ctx.CallActivityAsync<object[]>("RequestApproval"); using

    (var timeoutCts = new CancellationTokenSource()) { DateTime dueTime = ctx.CurrentUtcDateTime.AddHours(72); Task durableTimeout = ctx.CreateTimer(dueTime, 0, cts.Token); Task<bool> approvalEvent = ctx.WaitForExternalEvent<bool>("ApprovalEvent"); if (approvalEvent == await Task.WhenAny(approvalEvent, durableTimeout)) { timeoutCts.Cancel(); await ctx.CallActivityAsync("HandleApproval", approvalEvent.Result); } else { await ctx.CallActivityAsync("Escalate"); } } }
  20. [FunctionName("ProvisionNewDevices")] public static async Task ProvisionNewDevices([OrchestrationTrigger] DurableOrchestrationContext ctx) { string[]

    deviceIds = await ctx.CallActivityAsync<string[]>("GetNewDeviceIds"); // Run multiple device provisioning flows in parallel var provisioningTasks = new List<Task>(); foreach (string deviceId in deviceIds) { Task provisionTask = ctx.CallSubOrchestratorAsync("DeviceProvisioningOrchestration", deviceId); provisioningTasks.Add(provisionTask); } await Task.WhenAll(provisioningTasks); // ... }
  21. None
  22. None
  23. None
  24. None
  25. None
  26. List<SampleLinks> links = await context.CallActivityAsync<List<SampleLinks>>("LinkResolving_SampleLinks_GetAllLink s", null); List<Task<List<(int id, string

    resolvedLink)>>> tasks = new List<Task<List<(int id, string resolvedLink)>>>(); foreach (IEnumerable<SampleLinks> batch in links) { tasks.Add(context.CallActivityAsync<List<(int id, string newLink)>>("LinkResolving_ResolveLinkBatch", batch.Select(link => (link.Id, link.Link)))); } await Task.WhenAll(tasks); // . . .
  27. None
  28. None
  29. None
  30. None