I want to send second message to Actor as new Message - scala

I have two actors for example, sender:
class ActorSender(actroReciever: ActorRef) extends Actor{
implicit val timeout = Timeout(100, TimeUnit.SECONDS)
override def receive: Receive = {
case "RUN" => {
val resp = Await.result(actroReciever ? "Msg", 100.seconds)
println("receive response " + resp)
};
case str:String => println(str)
case _ => println("Error type msg")
}
}
reciever:
class ActroReciever extends Actor{
override def receive: Receive = {
case str:String => {
val snd = sender()
snd ! "MessageFirst"
snd ! "MessageSecond"
}
}
}
And class for starting:
object Tester extends App {
val system = ActorSystem("system")
val receiver = system.actorOf(Props[ActroReciever](new ActroReciever()), "receiver")
val sender = system.actorOf(Props[ActorSender](new ActorSender(receiver)), "sender")
sender ! "RUN"
}
I want to send two messages to sender(), first as reply for "ask", second msg as "new Message", which ActorSender execute as "matching", how I can do it? Thanks

First, you know you should not use Await, right?
Second, ask (?) is not meant to be used within actors. ask creates a temporary actor which can only receive a single message. It is not your ActorSender receiving the answer, but the temporary actor created by ask. That's why you have the feeling you can only send 1 answer.
You are doing this wrong, you should simply send your message using actroReciever ! "Msg". No need to change anything on your ActroReciever.

Related

Scala Akka Actor - Dead Letters encountered

I am using Scala with Akka actors. I know that an actor has a mailbox. So any communication with the actor is serialized. I have an actor that does a certain job--say it downloads an image.
class DownloadImageActor(implicit val injector: Injector) extends Actor with Injectable {
val imageDownloadService = inject[ImageDownloadService]
implicit val ec = inject[ExecutionContext]
override def receive: Receive = {
case DownloadImage(jobId, imageUrl) =>
imageDownloadService.downloadImage(imageUrl).onComplete {
case Success(image) =>
sender() ! ImageDownloadSuccess(imageUrl, image, jobId)
case Failure(e) =>
sender() ! ImageDownloadFail(imageUrl, e, jobId)
}
}
}
As you can see, the actor downloads the image in an async fashion. imageDownloadService.downloadImage returns a Future on whose completion a message is sent to the sender. Now this is where I get the dead letters encountered message.
Where have I gone wrong?
EDIT #1
The parent actor which sends message to the download actor
class ParentActor(implicit val injector : Injector) extends Actor with Injectable {
val downloadImageActor = inject[ActorRef](identified by "ImageDownloadActor")
override def receive: Receive = {
case DownloadImages(urls, _id) => urls.foreach(url =>
downloadImageActor ! DownloadImage(id, imageUrl = url)
)
case ImageDownloadSuccess(image : Image) =>
}
}
Don't know if any other problem, but the method you use sender in Future is wrong, you need to assign it to a new variable, then combine the onComplete callback it forms a closure which will not be override by other actor handle.
In your code, need to add lineA, and replace lineB, lineC with lineD, lineE. Or you may want to have a look for pipeTo function of Future.
class DownloadImageActor(implicit val injector : Injector) extends Actor with Injectable{
val imageDownloadService = inject[ImageDownloadService]
implicit val ec = inject[ExecutionContext]
override def receive: Receive = {
case DownloadImage(jobId, imageUrl) =>
val client = sender // lineA
imageDownloadService.downloadImage(imageUrl).onComplete {
//case Success(image) => sender() ! ImageDownloadSuccess(imageUrl, image, jobId) // lineB
//case Failure(e) => sender() ! ImageDownloadFail(imageUrl,e, jobId) // lineC
case Success(image) => client ! ImageDownloadSuccess(imageUrl, image, jobId) // lineD
case Failure(e) => client ! ImageDownloadFail(imageUrl,e, jobId) // lineE
}
}
}

Is it possible for the supervisor to pass the exception to the calling actor if the child actor fails even after retrying N times?

I have two actors Computer and Printer. Computer is the parent of Printer and has a one for one strategy defined for Printer.
I have listed the code below.
class Computer extends Actor with ActorLogging{
import Computer._
import Printer._
implicit val timeout: Timeout = 2 seconds
val printer: ActorRef = context.actorOf(Props[Printer], "printer-actor")
override def receive: Receive = {
case Print(text) => {
val printJob: Future[Any] = printer ? PrintJob(Random.nextInt, text)
printJob.mapTo[Page].map {
case Page(text) => {
log.info(s"Received page containing text ${text}")
context.system.shutdown()
}
}.onFailure {
case t: Throwable => sender ! akka.actor.Status.Failure(t)
}
}
}
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 3, withinTimeRange = 1 minute) {
case e : Exception => {
log.info(s"caught exception of type ${e.getClass}")
SupervisorStrategy.Restart
}
}
}
class Printer extends Actor with ActorLogging{
import Printer._
override def receive: Receive = {
case PrintJob(id, text) => {
log.info(s"Received ${PrintJob(id, text)}")
if (Random.nextBoolean) sender ! Page(text)
else throw new NoPaperException(id)
}
}
override def preRestart(cause: Throwable, message: Option[Any]) = {
log.info(s"Restarting actor ${self} because of ${cause}. Queueing message ${message}")
postStop()
message.map(self forward _)
}
}
The Printer throws an exception based on the random generator. The code works fine, the supervisor restarts the and retries the child actor on failure just as instructed.
However the ask pattern val printJob: Future[Any] = printer ? PrintJob(Random.nextInt, text) fails with a AkkaTimeoutException in case all attempts to get the Printer actor work fails.
Is there a way to pass back the exact exception which caused the actor to fail ? In this case NoPapperException.
Cheers,
Utsav
to pass the exception back to the sender you need to sender ! Status.Failure(e) - where e is the exception
You can either do that directly from the actor, or if you want to do that from the supervisor you need to have a subclass of exception that would hold the sender ref with it so that the supervisor would be able to send the exception back

postRestart and preRestart methods are not getting invoke in akka actots

I am following this tutorial here is my code
case class ArtGroupDeleteFromES (uuidList:List[String])
class ArtGroupDeleteESActor extends Actor{
val log = LoggerFactory.getLogger(this.getClass)
override def preStart() {
log.debug("preStart Starting ArtGroupDeleteESActor instance hashcode # {}",
this.hashCode())
}
override def postStop() {
log.debug("postStop Stopping ArtGroupDeleteESActor instance hashcode # {}",
this.hashCode())
}
override def preRestart(reason: Throwable, message: Option[Any]) {
log.debug("I am restarting")
log.debug("ArtGroupDeleteESActor: preRestart")
log.debug(s" MESSAGE: ${message.getOrElse("")}")
log.debug(s" REASON: ${reason.getMessage}")
super.preRestart(reason, message)
}
override def postRestart(reason: Throwable) {
log.debug("restart completed!")
log.debug("ArtGroupDeleteESActor: postRestart")
log.debug(s" REASON: ${reason.getMessage}")
super.postRestart(reason)
}
def receive = {
case ArtGroupDeleteFromES(uuidList) =>
throw new Exception("Booom")
sender ! true
}
case message =>
log.warn("Received unknown message: {}", message)
unhandled(message)
}
}
and here is the how i am sending this actor a message
class ArtGroupDeletionActor extends Actor{
val log = LoggerFactory.getLogger(this.getClass)
override val supervisorStrategy = OneForOneStrategy(
maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
case _:Exception => Restart
}
val artGroupDeleteESActor=context.actorOf(Props[ArtGroupDeleteESActor]
.withDispatcher("akka.actor.ArtGroupDeleteESActor-dispatcher")
,name = "ArtGroupDeleteESActor")
def receive = {
case DeleteArtGroup(uuidList) =>
val future1 = ask(artGroupDeleteESActor, ArtGroupDeleteFromES(uuidList)).mapTo[Boolean]
var isDeletedfromES = Await.result(future1, timeout.duration)
case message =>
log.warn("Unhandled message received : {}", message)
unhandled(message)
}
}
object test extends App{
val artGroupDeletionActor=system.actorOf(Props[ArtGroupDeletionActor]
.withDispatcher("akka.actor.ArtGroupDeletionActor-dispatcher")
,name = "ArtGroupDeletionActor")
artGroupDeletionActor ! DeleteArtGroup(List("123"))
}
the PostRestart() and preRestart() methods are not invoking,but preStart() and postStop() gets called, please guide me where i am doing wrong
(for simplicity I'll call your actors Parent and Child from now on)
What happens here is that when an exception occurs inside Child.receive, it doesn't send a response to Parent, instead, the actor system sends some control instruction for the supervision strategy. However, Parent is blocked on Await waiting for completion of future1, which only happens after the timeout exceeds, and then, in turn, a TimeoutException is thrown inside Parent.receive, killing (restarting) the Parent actor itself, and thus the supervising message of an exception in Child is then passed to deadLetters, never restarting the Child.
You should never, ever, ever block inside an actor, so this is incorrect:
val future1 = ask(artGroupDeleteESActor, ArtGroupDeleteFromES(uuidList)).mapTo[Boolean]
var isDeletedfromES = Await.result(future1, timeout.duration)
Instead, you have to either utilize some kind of message identification to distinguish one reply from another in concurrent environment, or add an onComplete to the Future and send a message to self in the closure (beware: no logic other than sending a message should be executed inside the closure to the Future!).
So, option A:
case class ArtGroupDeleteFromES(id: Long, uuidList: List[String])
case class ArtGroupDeleteFromESResult(id: Long, success: Boolean)
class Parent extends Actor {
override val supervisionStrategy = ...
var msgId = 0L
var pendingRequesters = Map.empty[Long, ActorRef]
val child = context.actorOf(Props[Child])
def nextId = {
msgId += 1
msgId
}
def receive = {
case DeleteArtGroup(uuidList) =>
val id = nextId
pendingRequesters += id -> sender() // store a reference to the sender so that you can send it a message when everything completes
child ! DeleteArtGroupFromES(nextId, uuidList)
case ArtGroupDeleteFromESResult(id, success) =>
// process result...
pendingRequesters(id) ! "done"
pendingRequesters -= id
}
}
And option B:
case class ArtGroupDeleteFromES(uuidList: List[String])
case class ArtGroupDeleteFromESResult(replyTo: ActorRef, success: Boolean)
class Parent extends Actor {
override val supervisionStrategy = ...
val child = context.actorOf(Props[Child])
def receive = {
case DeleteArtGroup(uuidList) =>
val requester = sender() // when the future completes, sender may have already changed, so you need to remember it
(child ? DeleteArtGroupFromES(uuidList)).onComplete {
case Success(success) => self ! ArtGroupDeleteFromESResult(requester, success)
case Failure(e) =>
log.warn("Could not delete...", e)
self ! ArtGroupDeleteFromESResult(requester, success = false)
}
}

Akka ask returns nothing when tell is used in future

The following code uses 2 asks and initially seems like it supposed to print "This should be printed in actor1 but most of the time wont because of a race condition with the receive method finishing".
However there seems to be a race condition between the 'receive' method finishing and the future finishing and thus nothing is printed.
Is this the intended behavior by akka? is this a bug?
I am trying to avoid using 'ask' as much as possible and using 'tell' instead, but sometimes it's a must.
import akka.actor._
import akka.routing.SmallestMailboxPool
import akka.util.Timeout
import akka.pattern.ask
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
object ActorsAskingStuff extends App {
val system = ActorSystem("abc-system")
val actor1 = system.actorOf(Props[Actor1].withRouter(SmallestMailboxPool(1)), "actor1")
actor1 ! 5
}
class Actor1 extends Actor {
implicit val timeout = Timeout(60 seconds)
val actor2: ActorRef = context.actorOf(Props[Actor2].withRouter(SmallestMailboxPool(1)), "actor2")
override def receive: Receive = {
case _ =>
println("actor1 was called")
actor2 ? "hello" onComplete {
case Success(a) => println(a)
case Failure(b) => println("FAILURE")
}
}
}
class Actor2 extends Actor {
implicit val timeout = Timeout(6 seconds)
val actor3: ActorRef = context.actorOf(Props[Actor3].withRouter(SmallestMailboxPool(1)), "actor3")
override def receive: Actor.Receive = {
case _ =>
println("actor2 was called")
actor3 ? "hello" map {
_ =>
println("Actor2 completed the future")
sender ! "This should be printed in actor1 but most of the time wont because of a race condition with the receive method finishing"
}
// uncomment this to make it work
//Thread.sleep(100)
}
}
class Actor3 extends Actor {
override def receive: Actor.Receive = {
case _ =>
println("actor3 was called")
sender ! "I'm actor3"
}
}
You are closing over the sender when you do this:
actor3 ? "hello" map {
_ =>
println("Actor2 completed the future")
sender ! "This should be printed in actor1 but most of the time wont because of a race condition with the receive method finishing"
}
At that point the sender could have changed. In order to avoid closing over the sender you can rewrite like this:
val originator = sender()
actor3 ? "hello" map {
_ =>
println("Actor2 completed the future")
originator ! "This should be printed in actor1 but most of the time wont because of a race condition with the receive method finishing"
}
Once you go into a Future based callback (like onComplete or map for example) at that point, the actor thinks it's done precessing the current message and moves onto the next message in the mailbox (if there is one). When this happens, the sender() which is just a def that returns the value of a var that is subject to change will return either a new ActorRef (more messages) or nothing (deadletter) if no messages.
You can checkout this post for more info:
sender inside a future

How to send iterables between actors or from an actor to a Future?

A future from the main method of a program sends a msg to its actor asking for an iterable object. The actor then creates another future that asks for the iterable object (say an ArrayBuffer) from a remote actor. After receiving the ArrayBuffer from the remote actor, how would the actor send it back to the first future in the main method? It seems creating a local alias of sender and creating a separate case class to represent the iterable does not prevent dead letters from being encountered.
Here is a sample code:
case class SequenceObject(sqnce:Seq[someArrayBuffer])
//...
implicit val timeout = Timeout(10 seconds)
val fut1: Future[Any] = myActor ? iNeedAnArrayBufferObject
fut1.onSuccess {
case listOfItems: SequenceObject => {
//do sth with listofItems.sqnce
}
class myActor extends Actor {
implicit val timeout = Timeout(1 seconds)
def receive = {
case a: iNeedAnArrayBufferObject => {
val originalSender = sender
val fut: Future[Any] = (remoteActor ? a)
fut.onSuccess {
case list: SequenceObject => {
originalSender ! SequenceObject(list.sqnce)
}
}
The remote actor code is:
class ServerActorClass extends Actor {
def receive = {
case a: iNeedAnArrayBufferObject => {
val closer = sender()
closer ! SequenceObject(ArrayBufferObject[information])
}
}
The above does not seem to work. The remote actor and the local actor can communicate and messages are received correctly. However, the iterable object is never send back to fut1. Why is that? Thanks in advance.
Check pipeTo pattern in Ask: Send-And-Receive-Future section