How does pattern matching work in Akka.Actor receive method? - scala

I am new to Scala programming and having some trouble understanding how actors works and how to use them correctly.
Looking at the source code of an Akka actor, the following is exposed:
trait Actor {
def receive: Actor.Receive // Actor.Receive = PartialFunction[Any, Unit]
}
My first impression of this is that Actor is a trait that exposes one abstract method, receive which takes no arguments and then returns a partial function. First question, is this the correct interpretation of the API?
Next, I looked at the documentation for how to implement actors. The examples look something like this:
class HelloActor extends Actor {
def receive = {
case "hello" => println("hello back at you")
case _ => println("huh?")
}
}
Clearly there is some pattern matching going on here, but I'm having trouble understanding how this works. For instance, let's say I wanted to invoke the receive method directly without using something like send, how would I do it?

as others already answered, you never should directly call methods on Actors. But your question seems to be more about "how does PartialFunction work in Scala?", right?
In Scala the PartialFunction[In, Out] has a bit of compiler generated code for methods like isDefinedAt, so you can ask a partial function if it's defined for a certain argument:
scala> val fun: PartialFunction[Any, String] = {
| case 42 => "yes"
| }
fun: PartialFunction[Any,String] = <function1>
scala> fun.isDefinedAt(12)
res0: Boolean = false
scala> fun.isDefinedAt(42)
res1: Boolean = true
scala> fun(42)
res2: String = yes
scala> fun(12)
scala.MatchError: 12 (of class java.lang.Integer)
```
We use the isDefinedAt method before we apply a message to a receive function, and if it's not able to handle the message we pass it into unhandled(msg), which usually just logs the message to dead letters so you can figure out that your Actor didn't handle a message.

For instance, let's say I wanted to invoke the receive method directly without using something like send, how would I do it?
You would not.
Directly invoking the receive function of an actor without going through its ActorRef breaks the guarantees of the actor model:
Actors process exactly one message at a time.
An actor's internal state can only be mutated from the receive function.
(among others, see What is an actor?)
When you send an ActorRef a message, you aren't directly talking to the actor. Hidden behind the ActorRef are any number of implementation details—you could be sending a message to a router or a remote actor or a dead actor (or all three!). Isolating an actor's details behind this reference allows the guarantees that are the entire point of the actor model.

Related

What is the type of an Akka (Typed) actor Behavior that handles no messages?

Suppose I am defining a Behavior for an Akka (Typed) actor that will perform a concurrent computation, report its result to the actor that spawned it, and then stop.
If I initialize this actor with all of the inputs to the computation, plus a reference to its "parent", then it will not ever need to receive an incoming message of any type.
I would create this Behavior with Behaviors.setup, passing it a function that performs the computation, and then returns Behaviors.stopped.
Akka (Typed) requires me to provide a result type of some Behavior[T] for the function that will return this Behavior. And while I could assign to T the type of the result message that the Behavior will send, that doesn't seem correct. Isn't T meant to constrain what messages an actor can receive? And therefore, if I define T in terms of the type of message that the actor will send, am I not "lying" to the compiler?
In fact, this Behavior cannot - and will not - handle any incoming messages whatsoever.
Is there a more correct type that I can assign to this Behavior?
Or am I somehow missing the point of typed actor behaviors?
Example:
In the code below, my apply returns Behavior[ Result ], but this is a lie. It won't ever receive or handle a Result message. (It will only send one.)
import akka.actor.typed.{ ActorRef, Behavior }
import akka.actor.typed.scaladsl.Behaviors
object ConcurrentComputation {
sealed trait Result
final case class Success( result : Double ) extends Result
final case class Failure( exception : Throwable ) extends Result
def apply(
argX : Double,
argY : Double,
parent : ActorRef[ Result ]
) : Behavior[ Result ] = Behaviors.setup(
context => {
try {
Success( compute( argX, argY ) )
}
catch {
case e : Throwable => Failure( e )
}
Behaviors.stopped
}
)
// Never mind what's actually being done.
def compute( x : Double, y : Double ) : Double = ???
}
In general it is used Behaviors.setup[Nothing] for behaviors that don´t receive any message. It is supposed that an actor receives messages, but sometimes they don´t. For example, when you create an ActorSystem in Akka Typed you need to provide a Behavior for the user guardian actor, this actor is not going to receive any message, and you create the behavior for that actor as Behavior[Nothing].
This type represents that this actor can not receive any message because "there exist no instances of this type" (Nothing type).
So far the best I've come up with is Behavior[ Any ].
This is bad semantics, since it expresses the opposite of the code's intent.
Practically speaking, I don't think it can harm the code execution, since no message can be or processed by the Behavior.
Better answers are welcome.

Akka: when is it safe to send a message

I am creating an actor via:
system.actorOf(Props(....))
or
system.actorOf(SmallestMailboxPool(instances).props(Props(....))).
I usually block the thread calling system.actorOf till actorSelection works.
Await.result(system.actorSelection("/user/" + name).resolveOne(), timeout.duration)
I am wondering if this is at all needed or I can immediately start using the actorRef and send (tell) messages to the actor/actor pool.
So the question boils down to, if I have an actorRef, does that mean the mailbox is already created or it might so happen that the messages I sent immediately after calling system.actorOf might get dropped?
If you drill down the the implementation of system.actorOf, you see a call to a method names makeChild. Internally, this utilizes a lengthy method on the ActorRefProvider trait (internally using LocalActorRefProvider) called actorOf. This rather lengthy method initializes the child actor. Relevant parts are:
val props2 =
// mailbox and dispatcher defined in deploy should override props
(if (lookupDeploy) deployer.lookup(path) else deploy) match {
case Some(d) ⇒
(d.dispatcher, d.mailbox) match {
case (Deploy.NoDispatcherGiven, Deploy.NoMailboxGiven) ⇒ props
case (dsp, Deploy.NoMailboxGiven) ⇒ props.withDispatcher(dsp)
case (Deploy.NoMailboxGiven, mbx) ⇒ props.withMailbox(mbx)
case (dsp, mbx) ⇒ props.withDispatcher(dsp).withMailbox(mbx)
}
case _ ⇒ props // no deployment config found
}
Or if a Router is explicitly provided:
val routerDispatcher = system.dispatchers.lookup(p.routerConfig.routerDispatcher)
val routerMailbox = system.mailboxes.getMailboxType(routerProps, routerDispatcher.configurator.config)
// routers use context.actorOf() to create the routees, which does not allow us to pass
// these through, but obtain them here for early verification
val routeeDispatcher = system.dispatchers.lookup(p.dispatcher)
val routeeMailbox = system.mailboxes.getMailboxType(routeeProps, routeeDispatcher.configurator.config)
new RoutedActorRef(system, routerProps, routerDispatcher, routerMailbox, routeeProps, supervisor, path).initialize(async)
Which means that once you get back an ActorRef, the mailbox has been initialized and you shouldn't be scared of sending it messages.
If you think about the semantics of what an ActorRef stands for, it would be a bit pointless to provide one with an ActorRef which is partly/not initialized. It would make system guarantees weak and would make the programmer think twice before passing messages, which is the opposite desire of the framework.

Akka message undelivered

I've a PropertiesStore actor that holds a mutable.Buffer of runtime configuration that's manipulated through a rest API. You read and write from said store using these objects:
Read(None, Property(key ="MyConfigKey", value = None))
to which the store responds with the Property and its value.
In order to ease the locating of this store, I have another actor (a cameo) that I create on the fly:
object PropertyLookup {
def apply(propertyKey: String, target: Option[ActorRef]): Props = {
Props(new PropertyLookup(propertyKey, target))
}
}
class PropertyLookup(key: String, target: Option[ActorRef]) extends Actor with ActorLogging {
val PropertiesStore = "/user/Master/DataStore/PropertiesStore"
val propertiesActor = context.actorSelection(PropertiesStore)
def receive: Actor.Receive = {
case _=>
log.info(s"Looking up ${key} via ${propertiesActor}")
propertiesActor.tell(Read(None, Property(key, None)), target.getOrElse(sender()))
log.info(s"Sent property lookup to PropertiesStore: ${key}")
// context.stop(self)
}
}
Which enables me to keep the "locating" of said actor (i.e. via its path) in one place, avoiding too much rework if it's moved. This PropertyLookup has an optional target actor that the PropertiesStore will send the result to once the lookup is performed. I use all this like so in another actor to get a config value before I do some work:
context.actorOf(PropertyLookup(PropertyKeys.SpreadsheetURL, None), s"PropertiesLookup_${self.path.name}") ! None
but when I do so, I get the following warning:
Message [domain.Read] from
Actor[akka://NN/user/$a/Master/DataStore/PlayerStore#-1100303450] to
Actor[akka://NN/user/Master/DataStore/PropertiesStore] was not
delivered. [2] dead letters encountered. This logging can be turned
off or adjusted with configuration settings 'akka.log-dead-letters'
and 'akka.log-dead-letters-during-shutdown'
and I never receive my Property instance in the callee. The cameo (instance of PropertyLookup) does do its stuff and log, so I know that part is working.
What's happening? Is my cameo all wrong? Any better ways to do this? How do I understand what's happening around this warning>
Is your code and log outputs up to date?
First you gave definition of Read as
Read(Property(key ="MyConfigKey", value = None))
but then you are using it as
Read(None, Property(key, None))
Secondly you are creating actor with "PropertiesLookup_${self.path.name}" in name. But your log output shows that this actor name doesn't contain "PropertiesLookup" string.
In addition, notice that your path as logged includes a $a which suggests that an anonymous actor is in fact in the path, which isn't what's in your actorSelection call.

how is receive implemented and defined

Sometimes there is so magic happens that I break my head figuring out on what the compiler does. For example in akka, receive is defined as:
def receive: Receive
type Receive = Actor.Receive
and Receive is defined as:
type Receive = PartialFunction[Any, Unit]
then we declare receive as:
def receive = {
case "a" => //do something
case "b" => //do something
case _ => //default
}
I am aware of PartialFunction but what I dont get is that how does it apply the message to receive. Aren't we suppose to provide apply and isDefinedAt because receive returns a PartialFunction?
Syntactically how does it apply receive to the message? DOes it do something like message match receive or something?
The compiler will automatically generate the isDefinedAt from the body and apply is the body itself. Where a partial function is expected, you can just write a block that consist of case expressions and the compiler will turn it into a partial function.
scala> val f: PartialFunction[Any, Any] = {
| case x: String => 3
| }
f: PartialFunction[Any,Any] = <function1>
scala> f.isDefinedAt("foo")
res1: Boolean = true
scala> f.isDefinedAt(23)
res2: Boolean = false
edit:
In the underlying akka code, receive will be called to set the handler function once and then for every arriving message it tries to apply the receive method and otherwise calls unhandled (see links).
So the handler will only be called, if it can handle the message, otherwise the message will be put in the deadletters mailbox.
edit2:
These are the relevant sections in the akka code:
Call handler if defined, otherwise call unhandled:
https://github.com/akka/akka/blob/master/akka-actor/src/main/scala/akka/actor/ActorCell.scala#L496
unhandled:
https://github.com/akka/akka/blob/master/akka-actor/src/main/scala/akka/actor/Actor.scala#L545

Scala Actors: Returning a Future of a type other than Any.

I am working my way through a book on Scala Actors, and I am running into a bit of a syntactical hangup. In practice, I tend to assign my variables and function definitions as such:
val v: String = "blahblahblah"
def f(n: Int): Int = n+1
including the (return)type of the item after its name. While I know this is not necessary, I have grown comfortable with this convention and find that it makes the code more easily understood by myself.
That being said, observe the below example:
class Server extends Actor {
def act() = {
while (true) {
receive {
case Message(string) => reply("Good,very good.")
}
}
}
}
def sendMsg(m: Message, s: Server): Future[String] = {
s !! m
}
The above code produces an error at compile time, complaining that the server returned a Future[Any], as opposed to a Future[String]. I understand that this problem can be circumvented by removing the return type from sendMsg:
def sendMsg(m: Message,s: Server) = s !! m
However, this is not consistant with my style. Is there a way that I can specify the type of Future that the server generates (as opposed to Future[Any])?
Your problem is a lot deeper than just style: you get a Future[Any] because the compiler cannot statically know better—with the current Akka actors as well as with the now deprecated scala.actors. In the absence of compile-time checks you need to resort to runtime checks instead, as idonnie already commented:
(actorRef ? m).mapTo[String]
This will chain another Future to the original one which is filled either with a String result, a ClassCastException if the actor was naughty, or with a TimeoutException if the actor did not reply, see the Akka docs.
There might be a way out soon, I’m working on an Akka extension to include statically typed channels, but that will lead to you having to write your code a little differently, with more type annotations.