Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Serverless Workflows with Durable Functions

Serverless Workflows with Durable Functions

Azure Functions are simple, short-lived, stateless, single-purpose actions that respond to events in the cloud.

But what if you need to implement a multi-step, long-running, stateful workflow? Aren't you able to reap the benefits of serverless? Or do you have to manage the orchestration of multiple isolated functions?

In this talk, Mikhail will demonstrate how Durable Functions combine Azure Functions and Durable Task to enable you to manage workflows. The toolbox includes "fan-out fan-in" pattern, waiting for external events and human interaction, error handling, retries and timeouts.

Mikhail Shilkov

November 29, 2018
Tweet

More Decks by Mikhail Shilkov

Other Decks in Programming

Transcript

  1. Microservices Event-Driven Cloud Serverless Function-as- a-Service Problems of Cloud Apps

    Activities & Orchestrators Activity Chaining Error Handling Parallelization External Events and Timers Conclusions
  2. Activity in C# [FunctionName("BookConference")] public static string BookConference( [ActivityTrigger] string

    name) { var ticket = BookingService.Book(name); return $"Ticket is {ticket}"; }
  3. Activity in C# [FunctionName("BookConference")] public static string BookConference( [ActivityTrigger] string

    name) { var ticket = BookingService.Book(name); return $"Ticket is {ticket}"; }
  4. Activity in C# [FunctionName("BookConference")] public static string BookConference( [ActivityTrigger] string

    name) { var ticket = BookingService.Book(name); return $"Ticket is {ticket}"; }
  5. Orchestrator in C# [FunctionName("TrivialOrchestrator")] public static async Task Trivial( [OrchestrationTrigger]

    DurableOrchestrationContext context) { await context.CallActivityAsync<string> ("BookConference", "Codemotion Milan"); }
  6. Orchestrator in C# [FunctionName("TrivialOrchestrator")] public static async Task Trivial( [OrchestrationTrigger]

    DurableOrchestrationContext context) { await context.CallActivityAsync<string> ("BookConference", "Codemotion Milan"); }
  7. Orchestrator in C# [FunctionName("TrivialOrchestrator")] public static async Task Trivial( [OrchestrationTrigger]

    DurableOrchestrationContext context) { await context.CallActivityAsync<string> ("BookConference", "Codemotion Milan"); }
  8. Multi-step Workflow public static async Task Sequential(DurableOrchestrationContext context) { var

    conf = await context.CallActivityAsync<ConfTicket> ("BookConference", "Codemotion Milan"); var flight = await context.CallActivityAsync<FlightTickets> ("BookFlight", conf.Dates); await context.CallActivityAsync("BookHotel", flight.Dates); }
  9. Behind the scenes: Storage 1. Orchestrator Started 2. ExecutionStarted: PlanTrip

    3. TaskScheduled: BookConference 4. OrchestratorCompleted 5. TaskCompleted: "Ticket is 000123" 6. OrchestratorStarted 7. TaskScheduled: BookFlight 8. OrchestratorCompleted 9. TaskCompleted: “Reservation UY9H2J"
  10. Stop-Resume Behaviour public static async Task Sequential(DurableOrchestrationContext context) { var

    conf = await context.CallActivityAsync<ConfTicket> ("BookConference", "Codemotion Milan"); var flight = await context.CallActivityAsync<FlightTickets> ("BookFlight", conf.Dates); await context.CallActivityAsync("BookHotel", flight.Dates); }
  11. Stop-Resume Behaviour public static async Task Sequential(DurableOrchestrationContext context) { var

    conf = await context.CallActivityAsync<ConfTicket> ("BookConference", "Codemotion Milan"); var flight = await context.CallActivityAsync<FlightTickets> ("BookFlight", conf.Dates); await context.CallActivityAsync("BookHotel", flight.Dates); }
  12. Stop-Resume Behaviour public static async Task Sequential(DurableOrchestrationContext context) { var

    conf = await context.CallActivityAsync<ConfTicket> ("BookConference", "Codemotion Milan"); var flight = await context.CallActivityAsync<FlightTickets> ("BookFlight", conf.Dates); await context.CallActivityAsync("BookHotel", flight.Dates); }
  13. Sub-orchestrators public static async Task SuperOrchestrator( DurableOrchestrationContext context) { await

    context.CallSubOrchestratorAsync ("BookTrip", codeMotionMilan); await context.CallSubOrchestratorAsync ("BookTrip", codeMotionMadrid); }
  14. Error Handling public static async Task ErrorHandling(DurableOrchestrationContext context) { await

    context.CallActivityAsync("BookConference", "Codemotion Milan"); try { var itinerary = MakeItinerary(...); await context.CallActivityAsync("BookFlight", itinerary); } catch (FunctionFailedException) { var alternativeItinerary = MakeAnotherItinerary(...); await context.CallActivityAsync("BookFlight", alternativeItinerary); } await context.CallActivityAsync("BookHotel", stay); }
  15. Error Handling public static async Task ErrorHandling(DurableOrchestrationContext context) { await

    context.CallActivityAsync("BookConference", "Codemotion Milan"); try { var itinerary = MakeItinerary(...); await context.CallActivityAsync("BookFlight", itinerary); } catch (FunctionFailedException) { var alternativeItinerary = MakeAnotherItinerary(...); await context.CallActivityAsync("BookFlight", alternativeItinerary); } await context.CallActivityAsync("BookHotel", stay); }
  16. Retries public static async Task Retries(DurableOrchestrationContext context) { await context.CallActivityAsync("BookConference",

    "Codemotion Milan"); var options = new RetryOptions( firstRetryInterval: TimeSpan.FromMinutes(1), maxNumberOfAttempts: 3); options.BackoffCoefficient = 2.0; await context.CallActivityWithRetryAsync("BookFlight", options, itinerary); await context.CallActivityAsync<string>("BookHotel", stay); }
  17. Fan-out and Fan-in (1) public static async Task Parallel(DurableOrchestrationContext cxt)

    { var milan = cxt.CallSubOrchestratorAsync ("BookTrip", codeMotionMilan); var madrid = cxt.CallSubOrchestratorAsync ("BookTrip", codeMotionMadrid); var expenses = await Task.WhenAll(milan, madrid); await cxt.CallActivityAsync("ReportExpenses", expenses); }
  18. Fan-out and Fan-in (1) public static async Task Parallel(DurableOrchestrationContext cxt)

    { var milan = cxt.CallSubOrchestratorAsync ("BookTrip", codeMotionMilan); var madrid = cxt.CallSubOrchestratorAsync ("BookTrip", codeMotionMadrid); var expenses = await Task.WhenAll(milan, madrid); await cxt.CallActivityAsync("ReportExpenses", expenses); }
  19. Fan-out and Fan-in (1) public static async Task Parallel(DurableOrchestrationContext cxt)

    { var milan = cxt.CallSubOrchestratorAsync ("BookTrip", codeMotionMilan); var madrid = cxt.CallSubOrchestratorAsync ("BookTrip", codeMotionMadrid); var expenses = await Task.WhenAll(milan, madrid); await cxt.CallActivityAsync("ReportExpenses", expenses); }
  20. Fan-out and Fan-in (2) public static async Task Parallel(DurableOrchestrationContext cxt)

    { var emailSendingTasks = recepients .Select(to => context.CallActivityAsync<bool>("SendEmail", to)) .ToArray(); var results = await Task.WhenAll(emailSendingTasks); if (results.All(r => r)) { /*...*/ } }
  21. Timers & Waiting for External Event Report Expenses At Schedule

    E-mail Result or Timeout Human Approves Timeout
  22. Timers & Waiting for External Event // Schedule an e-mail

    with link to approve/reject var sendAt = context.CurrentUtcDateTime.Add(OneDay); await context.CreateTimer(sendAt); await context.CallActivityAsync("SendEmail", approvalEmail); // Wait for result with timeout var activityTask = context.WaitForExternalEvent<bool>("Approval"); var timeoutTask = context.CreateTimer(deadline); var winner = await Task.WhenAny(activityTask, timeoutTask); // Determine the outcome return winner == activityTask && activityTask.Result;
  23. Timers & Waiting for External Event // Schedule an e-mail

    with link to approve/reject var sendAt = context.CurrentUtcDateTime.Add(OneDay); await context.CreateTimer(sendAt); await context.CallActivityAsync("SendEmail", approvalEmail); // Wait for result with timeout var activityTask = context.WaitForExternalEvent<bool>("Approval"); var timeoutTask = context.CreateTimer(deadline); var winner = await Task.WhenAny(activityTask, timeoutTask); // Determine the outcome return winner == activityTask && activityTask.Result;
  24. Timers & Waiting for External Event // Schedule an e-mail

    with link to approve/reject var sendAt = context.CurrentUtcDateTime.Add(OneDay); await context.CreateTimer(sendAt); await context.CallActivityAsync("SendEmail", approvalEmail); // Wait for result with timeout var activityTask = context.WaitForExternalEvent<bool>("Approval"); var timeoutTask = context.CreateTimer(deadline); var winner = await Task.WhenAny(activityTask, timeoutTask); // Determine the outcome return winner == activityTask && activityTask.Result;