Fsharp.Actor


Supervising Actors

Actors can supervise other actors, if we define an actor loop that fails on a given message

let err = 
        (fun (actor:IActor<string>) ->
            let rec loop() =
                async {
                    let! (msg,_) = actor.Receive()
                    if msg <> "fail"
                    then printfn "%s" msg
                    else failwithf "ERRRROROROR"
                    return! loop()
                }
            loop()
        )

then a supervisor will allow the actor to restart or terminate depending on the particular strategy that is in place

Strategies

A supervisor strategy allows you to define the restart semantics for the actors it is watching

OneForOne

A supervisor will only restart the actor that has errored

let oneforone = 
    Supervisor.spawn 
        <| Supervisor.Options.Create(actorOptions = Actor.Options.Create("OneForOne"))
    |> Supervisor.superviseAll [Actor.spawn (Actor.Options.Create("err_0")) err]

!!"err_0" <-- "fail"

// This yields
Restarting (OneForOne: actor://main-pc/err_0) due to error System.Exception: ERRRROROROR
    at FSI_0012.err@134-2.Invoke(String message) in D:\Appdev\fsharp.actor\samples\Actor.fsx:line 134
    at Microsoft.FSharp.Core.PrintfImpl.go@523-3[b,c,d](String fmt, Int32 len, FSharpFunc`2 outputChar, FSharpFunc`2 outa, b os, FSharpFunc`2 finalize, FSharpList`1 args, Int32 i)
    at Microsoft.FSharp.Core.PrintfImpl.run@521[b,c,d](FSharpFunc`2 initialize, String fmt, Int32 len, FSharpList`1 args)
    at Microsoft.FSharp.Core.PrintfImpl.capture@540[b,c,d](FSharpFunc`2 initialize, String fmt, Int32 len, FSharpList`1 args, Type ty, Int32 i)
    at Microsoft.FSharp.Core.PrintfImpl.gprintf[b,c,d,a](FSharpFunc`2 initialize, PrintfFormat`4 fmt)
    at FSI_0012.err@132-1.Invoke(String _arg1) in D:\Appdev\fsharp.actor\samples\Actor.fsx:line 134
    at Microsoft.FSharp.Control.AsyncBuilderImpl.args@753.Invoke(a a)
actor://main-pc/err_0 pre-stop Status: Errored
actor://main-pc/err_0 stopped Status: Shutdown
actor://main-pc/err_0 pre-restart Status: Restarting
actor://main-pc/err_0 re-started Status: OK

we can see in the last 4 lines that the supervisor restarted this actor.

OneForAll

If any watched actor errors all children of this supervisor will be told to restart.

let oneforall = 
    Supervisor.spawn 
        <| Supervisor.Options.Create(
                    strategy = Supervisor.Strategy.OneForAll,
                    actorOptions = Actor.Options.Create("OneForAll")
           )
    |> Supervisor.superviseAll
        [
            Actor.spawn (Actor.Options.Create("err_1")) err;
            Actor.spawn (Actor.Options.Create("err_2")) err
        ]
"err_1" ?<-- "Boo"
"err_2" ?<-- "fail"


// This yields
Restarting (OneForAll actor://main-pc/err_1) due to error System.Exception: ERRRROROROR
    at FSI_0004.err@134-2.Invoke(String message) in D:\Appdev\fsharp.actor\samples\Actor.fsx:line 134
    at Microsoft.FSharp.Core.PrintfImpl.go@523-3[b,c,d](String fmt, Int32 len, FSharpFunc`2 outputChar, FSharpFunc`2 outa, b os, FSharpFunc`2 finalize, FSharpList`1 args, Int32 i)
    at Microsoft.FSharp.Core.PrintfImpl.run@521[b,c,d](FSharpFunc`2 initialize, String fmt, Int32 len, FSharpList`1 args)
    at Microsoft.FSharp.Core.PrintfImpl.capture@540[b,c,d](FSharpFunc`2 initialize, String fmt, Int32 len, FSharpList`1 args, Type ty, Int32 i)
    at Microsoft.FSharp.Core.PrintfImpl.gprintf[b,c,d,a](FSharpFunc`2 initialize, PrintfFormat`4 fmt)
    at FSI_0004.err@132-1.Invoke(String _arg1) in D:\Appdev\fsharp.actor\samples\Actor.fsx:line 134
    at Microsoft.FSharp.Control.AsyncBuilderImpl.args@753.Invoke(a a)
actor://main-pc/err_2 pre-stop Status: OK
actor://main-pc/err_2 stopped Status: Shutdown
actor://main-pc/err_2 pre-restart Status: Restarting
actor://main-pc/err_2 re-started Status: OK
actor://main-pc/err_1 pre-stop Status: Errored
actor://main-pc/err_1 stopped Status: Shutdown
actor://main-pc/err_1 pre-restart Status: Restarting
actor://main-pc/err_1 re-started Status: OK

we can see here that all of the actors supervised by this actor has been restarted.

Fail

A supervisor will terminate the actor that has errored

let fail = 
    Supervisor.spawn 
        <| Supervisor.Options.Create(
                    strategy = Supervisor.Strategy.AlwaysFail,
                    actorOptions = Actor.Options.Create("Fail")
           )
    |> Supervisor.superviseAll
        [
            Actor.spawn (Actor.Options.Create("err_3")) err;
            Actor.spawn (Actor.Options.Create("err_4")) err
        ]

!!"err_3" <-- "fail"

// This yields
Terminating (AlwaysTerminate: actor://main-pc/err_1) due to error System.Exception: ERRRROROROR
    at FSI_0005.err@138-2.Invoke(String message) in D:\Appdev\fsharp.actor\samples\Actor.fsx:line 138
    at Microsoft.FSharp.Core.PrintfImpl.go@523-3[b,c,d](String fmt, Int32 len, FSharpFunc`2 outputChar, FSharpFunc`2 outa, b os, FSharpFunc`2 finalize, FSharpList`1 args, Int32 i)
    at Microsoft.FSharp.Core.PrintfImpl.run@521[b,c,d](FSharpFunc`2 initialize, String fmt, Int32 len, FSharpList`1 args)
    at Microsoft.FSharp.Core.PrintfImpl.capture@540[b,c,d](FSharpFunc`2 initialize, String fmt, Int32 len, FSharpList`1 args, Type ty, Int32 i)
    at Microsoft.FSharp.Core.PrintfImpl.gprintf[b,c,d,a](FSharpFunc`2 initialize, PrintfFormat`4 fmt)
    at FSI_0005.err@136-1.Invoke(String _arg1) in D:\Appdev\fsharp.actor\samples\Actor.fsx:line 138
    at Microsoft.FSharp.Control.AsyncBuilderImpl.args@753.Invoke(a a)
actor://main-pc/err_1 pre-stop Status: Errored
actor://main-pc/err_1 stopped Status: Shutdown

If you no longer require an actor to be supervised, then you can Unwatch the actor, repeating the OneForAll above

let oneforallunwatch = 
    Supervisor.spawn 
        <| Supervisor.Options.Create(
                    strategy = Supervisor.Strategy.OneForAll,
                    actorOptions = Actor.Options.Create("OneForAll")
           )
    |> Supervisor.superviseAll
        [
            Actor.spawn (Actor.Options.Create("err_5")) err;
            Actor.spawn (Actor.Options.Create("err_6")) err
        ]

Actor.unwatch !*"err_6" 

!!"err_5" <-- "fail"

// We now see that one actor `err_1` has restarted
Restarting (OneForAll actor://main-pc/err_1) due to error System.Exception: ERRRROROROR
    at FSI_0004.err@164-2.Invoke(String message) in D:\Appdev\fsharp.actor\samples\Actor.fsx:line 164
    at Microsoft.FSharp.Core.PrintfImpl.go@523-3[b,c,d](String fmt, Int32 len, FSharpFunc`2 outputChar, FSharpFunc`2 outa, b os, FSharpFunc`2 finalize, FSharpList`1 args, Int32 i)
    at Microsoft.FSharp.Core.PrintfImpl.run@521[b,c,d](FSharpFunc`2 initialize, String fmt, Int32 len, FSharpList`1 args)
    at Microsoft.FSharp.Core.PrintfImpl.capture@540[b,c,d](FSharpFunc`2 initialize, String fmt, Int32 len, FSharpList`1 args, Type ty, Int32 i)
    at Microsoft.FSharp.Core.PrintfImpl.gprintf[b,c,d,a](FSharpFunc`2 initialize, PrintfFormat`4 fmt)
    at FSI_0004.err@162-1.Invoke(String _arg1) in D:\Appdev\fsharp.actor\samples\Actor.fsx:line 164
    at Microsoft.FSharp.Control.AsyncBuilderImpl.args@753.Invoke(a a)
actor://main-pc/err_1 pre-stop Status: Errored
actor://main-pc/err_1 stopped Status: Shutdown
actor://main-pc/err_1 pre-restart Status: Restarting
actor://main-pc/err_1 re-started Status: OK
val err : actor:IActor<string> -> Async<'a>

Full name: Supervision.err
val actor : IActor<string>
Multiple items
type IActor =
  interface
    abstract member Link : IActor -> unit
    abstract member Post : obj * IActor option -> unit
    abstract member PostSystemMessage : SystemMessage * IActor option -> unit
    abstract member Start : unit -> unit
    abstract member UnLink : IActor -> unit
    abstract member UnWatch : unit -> unit
    abstract member Watch : IActor -> unit
    abstract member add_OnRestarted : Handler<IActor> -> unit
    abstract member add_OnStarted : Handler<IActor> -> unit
    abstract member add_OnStopped : Handler<IActor> -> unit
    ...
  end

Full name: FSharp.Actor.Types.IActor

--------------------
type IActor<'a> =
  interface
    inherit IActor
    abstract member Post : 'a -> unit
    abstract member Post : 'a * IActor option -> unit
    abstract member Receive : int option -> Async<'a * IActor option>
    abstract member Receive : unit -> Async<'a * IActor option>
  end

Full name: FSharp.Actor.Types.IActor<_>
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
val loop : (unit -> Async<'b>)
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
val msg : string
abstract member IActor.Receive : unit -> Async<'a * IActor option>
abstract member IActor.Receive : int option -> Async<'a * IActor option>
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val failwithf : format:Printf.StringFormat<'T,'Result> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.failwithf
val oneforone : IActor

Full name: Supervision.oneforone
module Supervisor

from FSharp.Actor
val spawn : options:Supervisor.Options -> IActor

Full name: FSharp.Actor.Supervisor.spawn
type Options =
  {MaxFailures: int option;
   Strategy: exn -> IActor<SupervisorMessage> -> IActor -> unit;
   ActorOptions: Options<SupervisorMessage>;}
  static member Create : ?maxFail:int option * ?strategy:(exn -> IActor<SupervisorMessage> -> IActor -> unit) * ?actorOptions:Options<SupervisorMessage> -> Options
  static member Default : Options

Full name: FSharp.Actor.Supervisor.Options
static member Supervisor.Options.Create : ?maxFail:int option * ?strategy:(exn -> IActor<SupervisorMessage> -> IActor -> unit) * ?actorOptions:Actor.Options<SupervisorMessage> -> Supervisor.Options
module Actor

from FSharp.Actor
type Options<'a> =
  {Id: string;
   Mailbox: IMailbox<ActorMessage<'a>>;
   Supervisor: IActor<SupervisorMessage> option;
   Logger: ILogger;
   Path: ActorPath;}
  static member Create : ?id:string * ?mailbox:IMailbox<ActorMessage<'a>> * ?supervisor:IActor<SupervisorMessage> * ?logger:ILogger * ?address:ActorPath -> Options<'a>
  static member Default : Options<'a>

Full name: FSharp.Actor.Actor.Options<_>
static member Actor.Options.Create : ?id:string * ?mailbox:IMailbox<ActorMessage<'a>> * ?supervisor:IActor<SupervisorMessage> * ?logger:ILogger * ?address:ActorPath -> Actor.Options<'a>
val superviseAll : actors:seq<IActor> -> sup:'a -> 'a (requires 'a :> IActor)

Full name: FSharp.Actor.Supervisor.superviseAll
val spawn : options:Actor.Options<'a> -> computation:(IActor<'a> -> Async<unit>) -> IActor

Full name: FSharp.Actor.Actor.spawn
val oneforall : IActor

Full name: Supervision.oneforall
module Strategy

from FSharp.Actor.Supervisor
val OneForAll : err:'a -> supervisor:IActor -> target:IActor -> unit

Full name: FSharp.Actor.Supervisor.Strategy.OneForAll
val fail : IActor

Full name: Supervision.fail
val AlwaysFail : err:'a -> supervisor:IActor -> target:IActor -> unit

Full name: FSharp.Actor.Supervisor.Strategy.AlwaysFail
val oneforallunwatch : IActor

Full name: Supervision.oneforallunwatch
val unwatch : actors:seq<IActor> -> unit

Full name: FSharp.Actor.Actor.unwatch
Fork me on GitHub