Akka actor - sender points to dead letters - scala

consider the following example:
case class Payload(message: String, async: Boolean)
class EchoActor extends Actor {
override def receive: Receive = {
case Payload(message, async) =>
if (async) Future {
println(s"from: $sender")
sender ! message
} else {
println(s"from: $sender")
sender ! message
}
}
}
def main(args: Array[String]): Unit = {
val system = ActorSystem("demo")
val echo = system.actorOf(Props[EchoActor])
implicit val timeout = Timeout(2 seconds)
(echo ? Payload("Hello", async = false)).mapTo[String].foreach(println(_))
(echo ? Payload("Async Hello", async = true)).mapTo[String].foreach(println(_))
StdIn.readLine()
system.terminate()
}
console output:
from: Actor[akka://demo/temp/$a]
Hello
from: Actor[akka://demo/deadLetters]
[INFO] [04/13/2017 19:56:58.516] [demo-akka.actor.default-dispatcher-4] [akka://demo/deadLetters] Message [java.lang.String] from Actor[akka://demo/user/$a#2112869650] to Actor[akka://demo/deadLetters] was not delivered. [1] 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'.
i.e. the sender points to deadLetters when accessing it from another thread.
What is the reason behind that?
is it a bug?
although, we can hold a reference to the actual sender to make it work:
if (async) {
val currentSender = sender()
Future {
println(s"from: $currentSender")
currentSender ! message
}
}
but... isn't there some better approach?

This is not a bug, but a documented behavior -
http://doc.akka.io/docs/akka/2.5.0/scala/actors.html#Send_messages
Using Future means invoking an anonymous function which is not an instance of the Actor class, therefor your sender() ref is mapped to the deadLetters mailbox

The better approach is the pipe pattern.
import akka.pattern.pipe
class EchoActor extends Actor {
override def receive: Receive = {
case Payload(message, async) =>
if (async) {
Future {
message
}.pipeTo(sender)
} else {
sender ! message
}
}
}
The issue is that sender is a function and its value is only valid if called on the same thread that is processing the incoming message. When you call sender within a future, it's being called from another thread and at another point in time, notably after the actor's receive function has already returned.
pipeTo captures the value of the current sender on the same thread as the actor and before the receive function has returned. This is effectively the same as your approach with the currentSender value.

Related

getting akka dead letters when using pipeTo between two actors

i have a use case in which i have a actor hierarchy
parent -> childABC -> workerchild
Now the worker child works and send its result to its parent(childABC which is a child of parent) and that child actor(childABC) send the result back to parent actor I am using pipeTo and getting dead letters here is my code
parent actor:
final case object GetFinalValue
class MyActor extends Actor{
import context.dispatcher
import akka.pattern.pipe
val log = LoggerFactory.getLogger(this.getClass)
val myManageActor = context.actorOf(Props[ManagerMyActor],"Managemyactor")
implicit val timeout = Timeout(ReadTimeIntervalValue.getInterval(), SECONDS)
override def receive: Receive = {
case GetFinalValue=>
ask(myManageActor,GetValue).pipeTo(sender())
case message =>
log.warn(" Unhandled message received : {}", message)
unhandled(message)
}
}
childABC (acc to example I gave above)
final case object GetValue
class ManagerMyActor extends Actor{
import context.dispatcher
import akka.pattern.pipe
val log = LoggerFactory.getLogger(this.getClass)
val myTokenActor = context.actorOf(Props[TokenMyActor2],"toknMyActor2")
implicit val timeout = Timeout(ReadTimeIntervalValue.getInterval(), SECONDS)
override def receive: Receive = {
case GetValue=>
ask(myTokenActor,CalculateValue).pipeTo(sender())
case message =>
log.warn(" Unhandled message received : {}", message)
unhandled(message)
}
}
child actor:
final case object CalculateValue
class TokenMyActor2 extends Actor{
import context.dispatcher
import akka.pattern.pipe
val log = LoggerFactory.getLogger(this.getClass)
override def receive: Receive = {
case CalculateValue=>
val future = Future{ "get the string"
}
val bac = future.map{result =>
sender ! result
}//.pipeTo(sender())
case message =>
log.warn("Actor MyActor: Unhandled message received : {}", message)
unhandled(message)
}
}
def main(args: Array[String]): Unit = {
implicit val timeout = Timeout(ReadTimeIntervalValue.getInterval(), SECONDS)
val myActor = system.actorOf(Props[MyActor],"myActor")
val future = ask(myActor, GetFinalValue).mapTo[String]
future.map {str =>
log.info ("string is {}",str)
}
Here are the logs:
[INFO] [akkaDeadLetter][01/12/2021 19:17:22.000] [api-akka.actor.default-dispatcher-5] [akka://api/deadLetters] Message [java.lang.String] from Actor[akka://api/user/myActor/Managemyactor/toknMyActor2#1239397461] to Actor[akka://api/deadLetters] was not delivered. [1] dead letters encountered. If this is not an expected behavior then Actor[akka://api/deadLetters] may have terminated unexpectedly. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
[INFO] [akkaDeadLetter][01/12/2021 19:17:41.989] [api-akka.actor.default-dispatcher-7] [akka://api/deadLetters] Message [akka.actor.Status$Failure] from Actor[akka://api/user/myActor#1829301550] to Actor[akka://api/deadLetters] was not delivered. [2] dead letters encountered. If this is not an expected behavior then Actor[akka://api/deadLetters] may have terminated unexpectedly. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
[INFO] [akkaDeadLetter][01/12/2021 19:17:41.996] [api-akka.actor.default-dispatcher-7] [akka://api/deadLetters] Message [akka.actor.Status$Failure] from Actor[akka://api/user/myActor/Managemyactor#-269929265] to Actor[akka://api/deadLetters] was not delivered. [3] dead letters encountered. If this is not an expected behavior then Actor[akka://api/deadLetters] may have terminated unexpectedly. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
Please guide me where am I mistaken, or pipeTo should not be used like this? if so what should i do to make it work
Not sure if it's intended or not but ask(myManageActor,GetValue).pipeTo(sender()) can be implemented as forward.
class MyActor extends Actor {
lazy val myManageActor: ActorRef = ???
override def receive: Receive = {
case GetFinalValue =>
myManageActor.forward(GetValue)
}
}
forward is the same as tell but it preserves the original sender of the messages.
This can be applied to MyActor and ManagerMyActor.
In the case of TokenMyActor2, you should not use
future.map{ result =>
sender ! result
}
as it it breaks akka context encapsulation, as specified in docs
When using future callbacks, such as onComplete, or map such as
thenRun, or thenApply inside actors you need to carefully avoid
closing over the containing actor’s reference, i.e. do not call
methods or access mutable state on the enclosing actor from within the
callback. This would break the actor encapsulation and may introduce
synchronization bugs and race conditions because the callback will be
scheduled concurrently to the enclosing actor. Unfortunately there is
not yet a way to detect these illegal accesses at compile time. See
also: Actors and shared mutable state
You should instead rely on Future(???).pipeTo(sender()), which is safe to use with sender().
After applying these changes, the code does work as expected
case object GetFinalValue
case object GetValue
case object CalculateValue
class MyActor extends Actor {
private val myManageActor: ActorRef =
context.actorOf(Props[ManagerMyActor], "myManageActor")
override def receive: Receive = { case GetFinalValue =>
myManageActor.forward(GetValue)
}
}
class ManagerMyActor extends Actor {
private val myTokenActor =
context.actorOf(Props[TokenMyActor2], "toknMyActor2")
override def receive: Receive = { case GetValue =>
myTokenActor.forward(CalculateValue)
}
}
class TokenMyActor2 extends Actor {
import context.dispatcher
override def receive: Receive = { case CalculateValue =>
val future = Future { "get the string" }
future.pipeTo(sender())
}
}
implicit val timeout = Timeout(3, SECONDS)
implicit val system = ActorSystem("adasd")
import system.dispatcher
val myActor = system.actorOf(Props[MyActor], "myActor")
val future = ask(myActor, GetFinalValue).mapTo[String]
future.foreach { str =>
println(s"got $str")
}
Produces got get the string.
As a final note, I'd advise not to use ask pattern within actors. The basic functionality of ask can be easily achieved with just tell and forward. Also the code is shorter and not overloaded with constant need of implicit val timeout
Just to add on top of the great post by #IvanStanislavciuc. You already noticed that you lose the reference to the sender in futures. A simple solution for that will be to keep it up front.
It means that changing in MyActor:
ask(myManageActor,GetValue).pipeTo(sender()) // Won't work
into:
val originalSender = sender()
ask(myTokenActor,CalculateValue).pipeTo(originalSender)
In ManagerMyActor, Change:
ask(myTokenActor,CalculateValue).pipeTo(sender()) // Won't work
into:
val originalSender = sender()
ask(myManageActor,GetValue).pipeTo(originalSender)
And in TokenMyActor2:
val originalSender = sender()
Future{ "get the string" }.pipeTo(originalSender)
Code run at Scastie.

Akka test - wait for actor initialization

case class FeatureFilter(s3Client: AmazonS3) extends Actor with ActorLogging {
override def preStart(): Unit = {
self ! Initialize
}
override def receive: Receive = {
case Initialize =>
// long running operaton
val tryfile = S3Connection(s3Client).downloadObject(...)
tryfile match {
case Success(file) =>
context.become(active(file))
case Failure(exception) =>
self ! PoisonPill
}
}
def active(file: File): Receive = {
case Query(key) =>
// do some processing and reply to sender
}
}
I am using below test for above actor:
"an actor" should {
// mocked S3 client
val client = ...
"test for presence of keys" in {
val actor = system.actorOf(Props(FeatureFilter(client)))
for (i <- 1 to 100) {
actor ! Query("test_string")
expectMsg(SomeMessage)
}
}
}
The above test fails with
java.lang.AssertionError: assertion failed: timeout (3 seconds) during expectMsg while waiting ...
I think this is because when the message actor ! Query("test_string") is sent to actor, it's handler is still receive, and so it doesn't respond, and hence the timeout.
But I even tried adding the handler for Query(key) in the receive method (just like in active method). Still I am getting the same error.
Could someone please point what is the issue here ?
Also when I move the S3 download task to preStart(), still the issue remains same. Isn't preStart() a blocking call ? How would the code in the test proceed until the preStart() is completed ?
akka stash sounds like the way you looking for. In case of any message that the actor support but is unhandled add on stash and unapply all if active is reached.
look at actor stash for documentation and example usage
may your code would look like
case msg => stash()
...
unstashAll()
context.become(active(file))

Akka: First message going to dead letters, from second message it is all fine

I have supervisor actor which selects child actor based on command received, whenever it creates a new child actor and sends message(ask pattern) it timesout as child actor tries to send back response but it goes to dead letters.
Here is the child actor code
class IdActor(id: String, injector: Injector) extends Actor {
override def receive: Receive = {
case cmd: GenerateIdCmd =>
val parent = sender()
...
Future(idEvent) pipeTo sender //Issue is here going to dead letters
//Future(idEvent) pipeTo parent //This also leads to same problem
//parent ! idEvent // Same issue
}
}
Here is supervisor code
class IdSupervisor(injector: Injector) extends Actor {
override def receive: Receive = {
case cmd: GenerateIdCmd =>
...
val ref = context.child(cmd.id).getOrElse {
context.actorOf(Props(classOf[IdActor], cmd.id, injector), cmd.id)
}
ask(ref, cmd) pipeTo sender
}
}
Second message is flowing back to originator, first response from all new child actors going to dead letters from there afterwards going good.
Working Solution
Issue is with Supervisor, fixed code
class IdSupervisor(injector: Injector) extends Actor {
override def receive: Receive = {
case cmd: GenerateIdCmd =>
val originator = sender
...
val ref = context.child(cmd.id).getOrElse {
context.actorOf(Props(classOf[IdActor], cmd.id, injector), cmd.id)
}
ask(ref, cmd) pipeTo originator
}
}
I suspect that the issue doesn't actually occur when a child replies to the parent as you describe, but when the parent sends a message to a child and expects a reply:
val ref = context.child(cmd.id).getOrElse {
context.actorOf(Props(classOf[IdActor], cmd.id, injector), cmd.id)
}
ask(ref, cmd) pipeTo sender
An actor is started asynchronously upon creation. The observation that, when a new child is created, the first message to that child results in dead letters, while subsequent messages to that child result in the intended behavior, suggests an actor initialization issue. What's probably happening is that the child actors receive their first GenerateIdCmd message before they have completely started.
Never ever pipe directly to sender from future inside actor, sender is def and at the moment when future is completed, it might be already different one than you expect. One of solution is to store sender before future call:
class IdActor(id: String, injector: Injector) extends Actor {
override def receive: Receive = {
case cmd: GenerateIdCmd =>
...
val originalSender = sender
Future(idEvent) pipeTo originalSender
}
}

Reply to sender goes to dead letter box

I have the following unit test:
it should "return xml" in new TestScope {
val testProbe: TestProbe = TestProbe()
val someActor = system.actorOf(Props[SomeActor])
testProbe.send(someActor, MakeXmlApiCall())
testProbe.expectMsgPF() {
case Success(message) => {
assert(message == "someMessage")
}
}
}
For the actor with this receive method:
override def receive: Receive = {
case MakeXmlApiCall() => {
val status = Future {"someMessage"}
println("In SomeActor")
status onComplete {
case Success(message) => {
sender ! message
}
}
}
}
"In SomeActor" is printed, which means that control reaches the receive method.
However, I am also getting this message:
[INFO] [12/15/2016 18:42:29.463] [testSystem-akka.actor.default-dispatcher-3] [akka://testSystem/deadLetters] Message [java.lang.String] from Actor[akka://testSystem/user/$a#-1159394947] to Actor[akka://testSystem/deadLetters] was not delivered. [1] 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'.
So why is the message sent to the dead-letter rather than the testProbe from the unit test.
Why is this happening?
Referencing sender() (or any actor state) from a callback or operator on a future is not safe, since sender() can have changed.
You should instead use pipe the future back to the sender: status.pipeTo(sender())
In addition, your expectMsgPF() call does not match on what you're actually sending from SomeActor. You're replying with a string, not an instance of Success.

How to send a delayed response inside Scala actor

Non-actor class sends a synchronous message to actor like this:
val response = WorkData !? "hello"
If i want to respond to this message right away, than i will do this:
receive {
case "hello" => reply("world")
}
But if i need to reply some time in the future, than
how do i store the caller reference, and send the reply after that?
Reference to the current caller is stored at sender. E.g., it's perfectly valid to replace
receive {
case "hello" => reply("world")
}
with
receive {
case "hello" => sender ! "world"
}
You can store this ref hereafter in the mutable variable, or pass recursively through the actor's loop.
I generally store the sender reference to use it later.
receive {
case "hello" =>
val otherParty = sender
// more receives, etc
// ...
otherParty ! "world"
}
How about just spawning an anonymous actor that processes the message and respond when it's ready? This way the receiver just acts as a dispatcher. This does not need a mutable variable to store anything since you are using a closure here.
import scala.actors.Actor
import scala.actors.Actor._
case class Message(msg: String)
class MyReceiver extends Actor {
def act() {
react {
case Message(msg) =>
actor {
sender ! process(msg)
}
}
}
def process(msg: String): String =
"Result: " + msg
}
object Main {
def main(args: Array[String]) {
val a = new MyReceiver
a.start()
val res = a !? Message("foo")
println(res)
}
}
Regards, raichoo