Coder Social home page Coder Social logo

durablefunctions.fsharp's Introduction

DurableFunctions.FSharp Library

F#-friendly API layer around Azure Functions Durable Extensions.

Note: this is an early draft with the primary goal to gather feedback, opinions, real-world scenarios and refine the API. Breaking changes may be introduced at any time.

Getting Started

Here is how you get started :

1. Create a new .NET Core console application

With .NET Core 2.1 installed, run the following command to create a new console application:

dotnet new console -lang F#

2. Modify fsproj

Edit the top section in fsproj to be:

<PropertyGroup>
  <TargetFramework>netcoreapp2.1</TargetFramework>
  <AzureFunctionsVersion>v2</AzureFunctionsVersion>
</PropertyGroup>

See the example.

3. Install NuGet package

Install the Microsoft.NET.Sdk.Functions and DurableFunctions.FSharp NuGet packages:

dotnet add package Microsoft.NET.Sdk.Functions
dotnet add package DurableFunctions.FSharp

4. Define an activity and an orchestrator

The following Hello World application can be used as a starting point:

namespace MyDurableApp

open Microsoft.Azure.WebJobs
open DurableFunctions.FSharp

module TypedSequence =

  let sayHello = 
    Activity.define "SayHello" (sprintf "Hello %s!")

  let workflow = orchestrator {
    let! hello1 = Activity.call sayHello "Tokyo"
    let! hello2 = Activity.call sayHello "Seattle"
    let! hello3 = Activity.call sayHello "London"

    // returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
    return [hello1; hello2; hello3]
  }

  [<FunctionName("SayHello")>]
  let SayHello([<ActivityTrigger>] name) = Activity.run sayHello

  [<FunctionName("TypedSequence")>]
  let Run ([<OrchestrationTrigger>] context: DurableOrchestrationContext) =
    Orchestrator.run (workflow, context)

5. Run

Install Azure Functions Core Tools to run the app locally and deploy to the cloud, or use the tooling in Visual Studio or Visual Studio Code.

If you have any issue, you can also clone/fork the samples.

Basic Orchestrator

Durable Orchestrators are not allowed to use async computation expression due to the requirement of being single-threaded. Orchestrators can be defined with task computation expression, as shown in the standard samples.

However, to enable a more F#-idiomatic style of orchestrator definitions, this library defines a new computation expression called orchestrator.

Given a simple activity function:

[<FunctionName("SayHello")>]
let SayHello([<ActivityTrigger>] name) = 
    sprintf "Hello %s!" name

An orchestrator can be defined as follows:

let workflow = orchestrator {
    let! hello1 = Activity.callByName<string> "SayHello" "Tokyo"
    let! hello2 = Activity.callByName<string> "SayHello" "Seattle"
    let! hello3 = Activity.callByName<string> "SayHello" "London"

    // returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
    return [hello1; hello2; hello3]
}

The result of the computation is a function of type DurableOrchestrationContext -> Task<'a> which can be invoked from the orchestrator Azure Function:

[<FunctionName("HelloSequence")>]
let Run ([<OrchestrationTrigger>] context: DurableOrchestrationContext) = 
    Orchestrator.run (workflow, context)

See the full example.

Typed Activity

In the example above, the orchestrator calls activities by name. It also specifies the return type explicitly.

To gain more type safety, an explicit typed Activity<'a, 'b> may be defined:

let sayHello = 
    Activity.define "SayTyped" (sprintf "Hello typed %s!")

Activity.define accepts the function name as the first parameter and the function 'a -> 'b as the second parameter. Azure Function still has to be defined separately:

[<FunctionName("SayTyped")>]
let SayHello([<ActivityTrigger>] name) = Activity.run sayHello name

The orchestrator can now infer types from the activity type:

let workflow = orchestrator {
    let! hello1 = Activity.call sayHello "Tokyo"
    let! hello2 = Activity.call sayHello "Seattle"
    let! hello3 = Activity.call sayHello "London"

    // returns ["Hello typed Tokyo!", "Hello typed Seattle!", "Hello typed London!"]
    return [hello1; hello2; hello3]
}

See the full example.

Async in activities

While Async<'a> return type is not supported out of the box by Azure Functions, it can be used internally. There is a helper defineAsync function to make such definition easier:

let hardWork = 
    fun item -> async {
      do! Async.Sleep 1000
      return sprintf "Worked hard on %s!" item
    }
    |> Activity.defineAsync "HardWork"

See the full example.

Orchestrator with an input parameter

Orchestrators can accept an input parameter (1 at most). This can be defined as an argument of the workflow definition function:

let workflow input = orchestrator {
  // ...
}

An overload of Orchestrator.run will get the input from the context and pass it to the workflow.

Fan-out/fan-in

Activity.all helper function can be used to run multiple activities in parallel and combine the results:

let workflow = orchestrator {
    let! items =
      ["Tokyo"; "Seattle"; "London"]
      |> List.map (Activity.call hardWork)
      |> Activity.all

    return String.concat ", " items
}

See the full example.

Delays

You can pause the orchestrator by calling Orchestrator.delay function:

let sendAndPause email = orchestrator {
    do! Activity.call sendNewsletter email
    do! Orchestrator.delay (TimeSpan.FromHours 1.0)
}

Note that the durable timer is used to implement this delay, so the orchestrator function will actually stop the current execution and will resume after the delay expires. See Timers in Durable Functions.

Retry

There is a built-in mechanism to retry failed activity calls. Invoke Activity.callWithRetries function with retry parameters:

let workflow = orchestrator {
    let policy = ExponentialBackOff { MaxNumberOfAttempts = 5
                                      FirstRetryInterval = TimeSpan.FromSeconds 1.
                                      BackoffCoefficient = 2. }
    return! Activity.callWithRetries policy failUntil3 "Jam"
}

See the full example.

Waiting For External Events

Orchestrator functions have the ability to wait and listen for external events. This feature of Durable Functions is often useful for handling human interaction or other external triggers.

Orchestrator.waitForEvent accepts the time-out duration and returns a Result<'a, string>: an Ok result if an external event occured, and Error otherwise:

let workflow = orchestrator {
    let maxWaitDuration = TimeSpan.FromHours 1.
    let! result = Orchestrator.waitForEvent maxWaitDuration "Ack"
    return 
        match result with
        | Ok Ack -> true
        | _ -> false
}

See the full example.

Contributions

Everybody is welcome to contribute! Please try the library and create an issue with your ideas, problems, suggestions and target scenarios.

durablefunctions.fsharp's People

Contributors

jbeeko avatar marcpiechura avatar mikhailshilkov avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.