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

Serverless Workflows with Azure Durable Functions

Serverless Workflows with Azure Durable Functions

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

But your applications are probably complex, multi-step, long-running, and stateful! Are you still able to reap the benefits of serverless? Do you have to hand-craft the interactions of stateless isolated functions yourself?

In this talk, Mikhail will demonstrate how Durable Functions combine Azure Functions and durable storage to implement workflows in the cloud. The toolbox includes "fan-in fan-out" pattern, waiting for external events and human interaction, error handling, retries and timeouts, and more.

Mikhail Shilkov

January 17, 2019
Tweet

More Decks by Mikhail Shilkov

Other Decks in Programming

Transcript

  1. Mikhail Shilkov Software Developer Microsoft Azure MVP Serverless Azure Functions

    Functional Programming F# @MikhailShilkov https://mikhail.io
  2. $

  3. public static async Task Sequential(DurableOrchestrationContext context) { var conf =

    await context.CallActivityAsync<ConfTicket> ("BookConference", "Ignite Singapore"); var flight = await context.CallActivityAsync<FlightTickets> ("BookFlight", conf.Dates); await context.CallActivityAsync("BookHotel", flight.Dates); }
  4. 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"
  5. public static async Task Sequential(DurableOrchestrationContext context) { var conf =

    await context.CallActivityAsync<ConfTicket> ("BookConference", "Ignite Singapore"); var flight = await context.CallActivityAsync<FlightTickets> ("BookFlight", conf.Dates); await context.CallActivityAsync("BookHotel", flight.Dates); }
  6. public static async Task SuperOrchestrator( DurableOrchestrationContext context) { await context.CallSubOrchestratorAsync

    ("BookTrip", igniteSingapore); await context.CallSubOrchestratorAsync ("BookTrip", igniteLondon); }
  7. public static async Task ErrorHandling(DurableOrchestrationContext context) { await context.CallActivityAsync("BookConference", "Ignite

    Singapore"); try { var itinerary = MakeItinerary(...); await context.CallActivityAsync("BookFlight", itinerary); } catch (FunctionFailedException) { var alternativeItinerary = MakeAnotherItinerary(...); await context.CallActivityAsync("BookFlight", alternativeItinerary); } await context.CallActivityAsync("BookHotel", stay); }
  8. public static async Task ErrorHandling(DurableOrchestrationContext context) { await context.CallActivityAsync("BookConference", "Ignite

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

    Singapore"); 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); }
  10. public static async Task Parallel(DurableOrchestrationContext cxt) { var milan =

    cxt.CallSubOrchestratorAsync ("BookTrip", igniteMilan); var berlin = cxt.CallSubOrchestratorAsync ("BookTrip", igniteBerlin); var bookings = await Task.WhenAll(milan, berlin); await cxt.CallActivityAsync("ReportExpenses", bookings); }
  11. // Schedule an e-mail with link to approve/reject var sendAt

    = context.CurrentUtcDateTime.Date.Add(OneDay); await context.CreateTimer(sendAt); await context.CallActivityAsync("SendEmail", email); // 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;
  12. // Schedule an e-mail with link to approve/reject var sendAt

    = context.CurrentUtcDateTime.Date.Add(OneDay); await context.CreateTimer(sendAt); await context.CallActivityAsync("SendEmail", email); // 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;
  13. // Schedule an e-mail with link to approve/reject var sendAt

    = context.CurrentUtcDateTime.Date.Add(OneDay); await context.CreateTimer(sendAt); await context.CallActivityAsync("SendEmail", email); // 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;
  14. // activity.js module.exports = function(context) { return `Hello ${context.bindings.name}!`; };

    // orchestrator.js const df = require("durable-functions"); module.exports = df.orchestrator(function*(context) { const output = []; output.push(yield context.df.callActivity("E1_SayHello", "Tokyo")); output.push(yield context.df.callActivity("E1_SayHello", "Seattle")); output.push(yield context.df.callActivity("E1_SayHello", "London")); return output; });
  15. // activity.js module.exports = function(context) { return `Hello ${context.bindings.name}!`; };

    // orchestrator.js const df = require("durable-functions"); module.exports = df.orchestrator(function*(context) { const output = []; output.push(yield context.df.callActivity("E1_SayHello", "Tokyo")); output.push(yield context.df.callActivity("E1_SayHello", "Seattle")); output.push(yield context.df.callActivity("E1_SayHello", "London")); return output; });
  16. // Activity let sayHello = Activity.define "SayHello" (sprintf "Hello %s!")

    // Orchestrator let workflow = orchestrator { let! hello1 = Activity.call sayHello "Tokyo" let! hello2 = Activity.call sayHello "Seattle" let! hello3 = Activity.call sayHello "London" return [hello1; hello2; hello3] }