akka.net
akka.net copied to clipboard
The IActorRef of a child dies or restarts could not be retrieve by mailbox.Sender() in parent
======================================== Version of Akka.NET? 1.5.0-Alpha Which Akka.NET Modules? Akka or Akka.FSharp
The IActorRef of a child dies or restarts could not be retrieve by mailbox.Sender() in parent
======================================== Reproduce
#if INTERACTIVE
#r @"nuget: Akka"
#r @"nuget: Akka.FSharp"
#endif
open System
open Akka.Actor
open Akka.Configuration
open Akka.FSharp
type CustomException() =
inherit Exception()
type Message =
| Echo of string
| Crash
let system = System.create "system" (Configuration.defaultConfig())
// create parent actor to watch over jobs delegated to it's child
let parent =
spawnOpt system "parent"
<| fun parentMailbox ->
// define child actor
let child =
spawn parentMailbox "child" <| fun childMailbox ->
childMailbox.Defer (fun () -> printfn "Child stopping")
printfn "Child started"
let rec childLoop() =
actor {
let! msg = childMailbox.Receive()
match msg with
| Echo info ->
// respond to original sender
let response = "Child " + (childMailbox.Self.Path.ToStringWithAddress()) + " received: " + info
childMailbox.Sender() <! response
| Crash ->
// log crash request and crash
//printfn "Child %A received crash order" (childMailbox.Self.Path)
raise (CustomException())
return! childLoop()
}
childLoop()
// define parent behavior
let rec parentLoop() =
actor {
let! (msg: Message) = parentMailbox.Receive()
child.Forward(msg) // forward all messages through
//###########################################
//###### Notice this sender()
let sender = parentMailbox.Sender()
if sender <> null then
printfn "sender: %A" sender
//###########################################
return! parentLoop()
}
parentLoop()
// define supervision strategy
<| [ SpawnOption.SupervisorStrategy (
// restart on Custom Exception, default behavior on all other exception types
Strategy.OneForOne(fun e ->
match e with
| :? CustomException -> Directive.Restart
| _ -> SupervisorStrategy.DefaultDecider.Decide(e)
//Directive.Restart
)
) ]
parent <! Crash
=> See "Notice this sender()"
Expected behavior
The parentMailbox.Sender() should give us the IActorRef of child
Actual behavior
The parentMailbox.Sender() is null
Environment
Windows Server 2019 + .NET 7.0
According to fault-tolerance
If the strategy is declared inside the supervising actor (as opposed to within a companion object) its decider has access to all internal state of the actor in a thread-safe fashion, including obtaining a reference to the currently failed child (available as the Sender of the failure message).
Workaround:
derive the SupervisorStrategy class and put the error handler inside it...
type SS () =
inherit SupervisorStrategy ()
override u.Decider :IDecider =
//Unchecked.defaultof<IDecider>
SupervisorStrategy.DefaultDecider
override u.Handle(child:IActorRef, exn:Exception) =
//Unchecked.defaultof<Directive>
Directive.Restart
override u.ProcessFailure(
context:IActorContext,
restart:bool,
child:IActorRef,
cause:Exception,
stats:ChildRestartStats,
children:IReadOnlyCollection<ChildRestartStats>) =
printfn "====> %A" child
override u.HandleChildTerminated(
actorContext:IActorContext,
child:IActorRef,
children:IEnumerable<IInternalActorRef>) =
printfn "====> %A" child
override u.ToSurrogate(system:ActorSystem) =
Unchecked.defaultof<ISurrogate>
<| [ SpawnOption.SupervisorStrategy (SS ()) ]
It seems like we can override the method to send the IActorRef back to parent's mailbox... But this is not convinient like the Sender() method... (illustrated in the document)