Actor cannot receive message sent to `mailbox.Sender ()`? - f#

I created the following test code - .Net core 2.1 console application. It prints the following message only
TestActor received message MyTask ("Test1","Test1") from [akka://MySystem/user/Scheduler#1426101451]
But the message Ok 0 cannot be received by the actor scheduler?
open System
open Akka.FSharp
open Akka
type MyTask = MyTask of item1: string * item2: string
let system = System.create "MySystem" <| Configuration.load ()
let scheduler (actors: Actor.IActorRef) (mailbox: Actor<Result<int, string>>) =
let rec loop (list: int list list) = actor {
let! m = mailbox.Receive ()
let sender = mailbox.Sender ()
let akkaName = mailbox.Self.Path.Name
printfn "%s received message %A from %A" akkaName m sender
return! loop []
}
actors <! MyTask("Test1", "Test1")
loop []
let processor (mailbox: Actor<MyTask>) =
let rec loop () = actor {
let! m = mailbox.Receive ()
let sender = mailbox.Sender ()
let akkaName = mailbox.Self.Path.Name
printfn "%s received message %A from %A" akkaName m sender
sender <! Ok 0 // scheduler cannot receive this message?
return! loop ()
}
loop ()
[<EntryPoint>]
let main argv =
let actor = spawn system "TestActor" processor
spawn system "Scheduler" (scheduler actor) |> ignore
system.WhenTerminated.Wait()
0
Update:
It works after I changed the parameter from (mailbox: Actor<Result<int, string>>) to (mailbox: Actor<_>)?

The scheduler isn't the sender of the MyTask message to which the processor is replying, because you are doing the tell (<!) from outside the actor computation. That means it's basically being sent with no sender. You can use the Tell method on IActorRef to send with an explicit sender, since you want to send it from outside the context of your actor computation:
actors.Tell(MyTask("Test1", "Test1"), mailbox.Self)
EDIT
Another issue is that the mailbox parameter to the scheduler is typed as Actor<Result<int, string>>, but since Ok 0 will be inferred as Result<int,obj> in your context, it won't match the actor type signature, and the message will be ignored. When defining an actor with a specific message type, any messages of a different type will be ignored.

Related

How to create a child actor in akka.net using F#?

I have two actors - childActor and parentActor
open System
open Akka
open Akka.FSharp
let systemActor = System.create "systemActor" <| Configuration.defaultConfig()
let childActor (mailbox: Actor<_>) =
let rec loop() = actor {
let! message = mailbox.Receive()
printfn "Message received"
return! loop()
}
loop()
let parentActor (mailbox: Actor<_>) =
let rec loop() = actor {
let! message = mailbox.Receive()
printfn "Message received"
return! loop()
}
loop()
I can create the parent actor reference using spawn function.
let parentRef = spawn systemActor "parentActor" parentActor
Now what I want to do here is to create the child actor reference under parent actor. Something like below.
let childRef = spawn parentRef "childActor" childActor
But this is not working. spawn function requires a parameter of type IActorRefFactory so it is not accepting IActorRef. Is there any way to create a child actor under parent actor in akka.net using F#?
IActorRefFactory is an interface responsible for determining a parent and in case of Akka.FSharp it's implemented by ActorSystem and Actor<_> as well. So in your case just use:
let childRef = spawn mailbox "childActor" childActor

F# and UdpClient reciver

I am creating UDP receiver in f# using .Net UdpCLient class and it looks simple:
let Start (ip: IPAddress, port : int32) : Async<unit> =
async {
try
let endpoint = IPEndPoint(ip, port)
use receivingClient = new UdpClient();
receivingClient.Client.Bind(endpoint)
let! receiveResult = receivingClient.ReceiveAsync() |> Async.AwaitTask
let receiveBytes = receiveResult.Buffer
printfn "%A" receiveBytes
with | ex -> raise (ex)
}
And to keep it alive I am using another property that uses rec function in it and it looks like:
let Watcher (ip: IPAddress, port : int32) : unit =
let rec listenerWatcher () =
async {
try
do! Start (ip, port)
return! listenerWatcher()
with | :? UdpClientDisposedException ->
return ()
}
listenerWatcher() |> Async.Start
and call with type is simple:
UdpReceiver.Watcher (ip, port) (* where UdpReceiver is module name *)
My problem is that I am only receiving first incoming package, like listener is shutting down after receiving first one, what could be the problem?
Maybe your problem is that you are sending packages too fast. After receiving the first package, it takes time to start again the receiver, but in the meanwhile the sender is still sending the next packages.
Not sure what is your exact intention, but I think you should start (setup) the receiver only one time, then repeatedly receive the incoming packages, and only restart the receiver when something wrong happens (exceptions are thrown).
By the way, your code is not really idiomatic in F#, you should:
Prefer separated parameters over tuples, it increases the chance of using currying.
Use type annotation only when needed, it makes code shorter.
Name functions so that they are verbs not nouns, and use camelCase style.
I would rewrite your code as below:
let start (ip: IPAddress) port =
let endpoint = IPEndPoint (ip, port)
let receivingClient = new UdpClient ()
receivingClient.Client.Bind endpoint
let rec loop () = async {
printfn "Waiting..."
let! receiveResult = receivingClient.ReceiveAsync () |> Async.AwaitTask
let receiveBytes = receiveResult.Buffer
printfn "Receive: %A" receiveBytes
return! loop ()
}
loop ()
let watch ip port =
let rec loop () = async {
try
return! start ip port
with ex ->
printfn "Error: %s" ex.Message
return! loop ()
}
loop ()
// in main function or somewhere:
watch ... ... |> Async.Start...

Why isn't my actor receiving a message?

Issue:
I am struggling to understand why my Reporter actor is not receiving messages based on the following statement that's in my Generator actor:
reporter <! Message input
My reporter actor is the following:
let reporterActor (mailbox:Actor<_>) =
let rec loop() = actor { let! msg = mailbox.Receive()
match msg |> box :?> Command with
| Start -> ()
| Message v -> printf "%s" v
| Exit -> mailbox.Context.System.Terminate() |> ignore }
loop() |> ignore
Basically, a console is launched that accepts input from the user. My Generator actor forwards that input to my Reporter actor. However, the code above never gets executed.
The code is the following:
module Main
open System
open Akka.FSharp
open Akka.Actor
open Actors
type Command =
| Message of string
| Start | Exit
let reporterActor (mailbox:Actor<_>) =
let rec loop() = actor { let! msg = mailbox.Receive()
match msg |> box :?> Command with
| Start -> ()
| Message v -> printf "%s" v
| Exit -> mailbox.Context.System.Terminate() |> ignore }
loop() |> ignore
let generatorActor (reporter:IActorRef) (mailbox:Actor<_>) message =
let handle input = match input with
| "exit" -> mailbox.Context.System.Terminate |> ignore
| _ -> reporter <! Message input
handle (Console.ReadLine().ToLower())
[<EntryPoint>]
let main argv =
let system = System.create "system" (Configuration.load())
let reporterActor = spawn system "reporterActor" (actorOf(reporterActor))
let generatorActor = spawn system "generatorActor" (actorOf2(generatorActor reporterActor))
generatorActor <! Start
system.AwaitTermination ()
0
Update:
I learned that I could trigger my Reporter actor by replacing the mailbox parameter with an arbitrary message parameter:
let reporterActor message =
match message |> box :?> Command with
| Start -> ()
| Message v -> printf "Reporting: %s" v
| Exit -> failwith "Kill this!"
I still don't understand when I should use a mailbox parameter versus when I should rely on a message parameter.
The difference is in how actorOf and actorOf2 work.
actorOf in conjunction with spawn creates an actor as a child of the root of the system which will handle messages with the function 'Message -> unit that was passed to it.
actorOf2 in in conjunction with spawn creates an actor as a child of the actor that you passed in and the child will handle the messages with the function 'Message -> unit that was passed.
Your original function signature for reporter actor was:
Actor<'Message> -> unit
and you used spawn system "reporterActor" (actorOf(reporterActor))
In this case you were saying that the message type that the new actor that was created would receive would be of type Actor<'Message> . This compiled because actorof just expects a function that takes a 'Message, and a 'Message is generic therefore Actor<'Message> satisfied the 'Message parameter.
When you updated the signature of reporterActor you change the signature to 'Message -> unit which is what actorOf is actually intended to accept.
In short generics allowed your code to compile since 'Message isn't really restricted, nor should it really be.
From: http://getakka.net/docs/FSharp%20API
actorOf (fn : 'Message -> unit) (mailbox : Actor<'Message>) :
Cont<'Message, 'Returned> - uses a function, which takes a message as
the only parameter. Mailbox parameter is injected by spawning
functions.
actorOf2 (fn : Actor<'Message> -> 'Message -> unit) (mailbox :
Actor<'Message>) : Cont<'Message, 'Returned> - uses a function, which
takes both the message and an Actor instance as the parameters.
Mailbox parameter is injected by spawning functions. Example:
> let handleMessage (mailbox: Actor<'a>) msg =
> match msg with
> | Some x -> printf "%A" x
> | None -> ()
>
> let aref = spawn system "my-actor" (actorOf2 handleMessage) let
> blackHole = spawn system "black-hole" (actorOf (fun msg -> ()))
spawn (actorFactory : IActorRefFactory) (name : string) (f :
Actor<'Message> -> Cont<'Message, 'Returned>) : IActorRef - spawns an
actor using a specified actor computation expression. The actor can
only be used locally.
All of these functions may be used with either the actor system or the
actor itself. In the first case the spawned actor will be placed under
/user root guardian of the current actor system hierarchy. In the
second option the spawned actor will become a child of the actor used
as the actorFactory parameter of the spawning function.

How to pattern match on the type of the message received in F# akka.net?

Please see last edit.
Apologies for the newbie question. I am trying to implement something in F# using Akka.net. I'm very new to F# and I have only used Akka from Scala. Basically I am trying to implement something that's pretty easy in Scala, namely making an Actor do different things based on the type of message it receives.
My code is below and it's a slight modification of the hello world example lifted from the akka.net website. I believe a first problem with my code is that it does record pattern matching instead of type pattern matching, however I was unable to write a type match one without compilation errors... Any help will be greatly appreciated. Thank you.
open Akka.FSharp
open Actors
open Akka
open Akka.Actor
type Entries = { Entries: List<string>}
let system = ActorSystem.Create "MySystem"
let feedBrowser = spawn system "feedBrowser" <| fun mailbox ->
let rec loop() = actor {
let! msg = mailbox.Receive()
match msg with
| { Entries = entries} -> printf "%A" entries
| _ -> printf "unmatched message %A" msg
return! loop()}
loop()
[<EntryPoint>]
let main argv =
feedBrowser <! "abc" // this should not blow up but it does
system.AwaitTermination()
0
Edit: the error is a runtime one, System.InvalidCastException, unable to cast object of type String to Entries.
Later edit: I got this to work with this change, downcasting to Object:
let feedBrowser = spawn system "feedBrowser" <| fun mailbox ->
let rec loop() = actor {
let! msg = mailbox.Receive()
let msgObj = msg :> Object
match msgObj with
| :? Entries as e -> printfn "matched message %A" e
| _ -> printf "unmatched message %A" msg
return! loop()}
loop()
Now these two lines work correctly
feedBrowser <! "abc"
feedBrowser <! { Entries = ["a"; "b"] }
the first one prints "unmatched message abc" and the second outputs the entries.
Is there a better way of going about this, without the cast? Does akka.net have something specifically for this case?
Thank you.
You should use a Discriminated Union (the Command type in this example). Then you can pattern match its options.
type Entries = { Entries: List<string>}
type Command =
| ListEntries of Entries
| OtherCommand of string
let stack() =
let system = ActorSystem.Create "MySystem"
let feedBrowser = spawn system "feedBrowser" <| fun mailbox ->
let rec loop() = actor {
let! msg = mailbox.Receive()
match msg with
| ListEntries { Entries = entries} -> printf "%A" entries
| OtherCommand s -> printf "%s" s
return! loop() }
loop()
And to send the message you should use:
feedBrowser <! OtherCommand "abc"
feedBrowser <! ListEntries { Entries = ["a"; "b"] }
It's important to say that the send operator has the following signature:
#ICanTell -> obj -> unit
So, if you pass an message with a different type, like a string, it'll raise an exception.

Why is my mailBoxProcessor stuck at the receive method?

I am using F# mailBoxProcessor to asynchronously process messages received from multiple network ends.
The code works as expected until I added function call getTreasuryYield after inbox.receive().
It gets stuck every time at inbox.receive() after running for a few seconds.
GetTreasuryYield is a quite slow method since it involves database and IO operations, but I
still do not understand how it gets stuck.
Any HELP will be appreciated.
let start rvSetting (agent:Agent<Message>) messageSelector=
try
TIBCO.Rendezvous.Environment.Open()
let _transport = new NetTransport(rvSetting.rvService, rvSetting.rvNetwork, rvSetting.rvDaemon)
let _listener = new Listener(TIBCO.Rendezvous.Queue.Default, _transport, rvSetting.rvSubject, null)
_listener.MessageReceived.Add(fun args->
printfn "before sent"
if messageSelector(args.Message) then
printfn "Message sent to agent: %A" args.Message
agent.Post(args.Message))
let rec dispatch() =
async{
try
TIBCO.Rendezvous.Queue.Default.Dispatch()
return! dispatch()
with
| e -> _log.Error(e.ToString())
}
Async.Start(dispatch())
with
|e -> printfn "%A" e.Message
_log.Error(e.Message)
let agent = new Agent<Message>(fun inbox ->
let rec loop() =
async{
let! (m : Message) = inbox.Receive()
// This line causes the problem
printfn "%A" (getTreasuryYieldFromMessage m)
Async.Start(treasuryAction m)
return! loop()
}
loop())
agent.Error.Add raise
[<EntryPoint>]
let main argv =
//start rvCorporate agent (fun x -> true)
agent.Start()
start rvTreasury agent treasurySelector
Console.ReadLine() |> ignore
0

Resources