Akka context.parent unexpected value - scala

The context.parent reference does not do it, for me. In a Play project, I launch a new actor to deal with each incoming request:
val myActor: ActorRef = system.actorOf(Props(classOf[MyActor])
val future: Future[String] = (myActor ? Hello)(5.seconds).mapTo[String]
future.map(result => Ok(result)).recover {
case ex: AskTimeoutException => Ok("timeout expired")
case _ => Ok("error")
}
This triggers more actor messaging in the receive method of myActor: when anotherActor responds to myActor, the latter sends a message AllGood to its parent.
class MyActor extends Actor {
// other stuff, among which setup of anotherActor
def receive: Actor.Receive = {
case Hallo => anotherActor ! Hello
case HelloBack => context.parent ! AllGood
}
}
class AnotherActor extends Actor {
def receive: Actor.Receive = {
case Hallo => sender ! HelloBack
}
}
Everything works fine until myActor tries to send a message to context.parent. I get a deadLetters message in the console.
from Actor[xyz] to Actor[akka://my-system/user] was not delivered. [1] dead letters encountered.
I can make it work if myActor keeps a reference to the original sender, and the code looks like this instead:
class MyActor extends Actor {
var myDad: Option[ActorRef] = None // <--- NEW REFERENCE
// other stuff, among which setup of anotherActor
def receive: Actor.Receive = {
case Hallo => {
myDad = Some(sender)
anotherActor ! Hello
}
case HelloBack => myDad ! AllGood <-- MYDAD INSTEAD OF CONTEXT.PARENT
}
}
class AnotherActor extends Actor {
def receive: Actor.Receive = {
case Hallo => sender ! HelloBack
}
}
The reference myDad is actually something like Actor[akka://my-system/temp/$a], while I would expect it to be the same as the context.parent which was Actor[akka://my-system/user]. Isn't it the same actor?
Can anybody explain this behaviour to me, and suggest a cleaner way to fix this? Thanks.

Looks like you mixed up the concepts of parent and sender.
context.parent [akka://my-system/user] is normal because the actor has been created as a child of the Akka system using system.actorOf(Props(classOf[MyActor]).
the sender method gives you back the actor who sent the message. The important thing here is; this is not the actor system who sent the message to your actor. When using the ask pattern Akka creates a temporary actor (in your case Actor[akka://my-system/temp/$a]) that will send the message and wait for the answer. When the response is received, the Future is completed.
If you want it is possible to forward the original sender so that your AnotherActor can reply directly HelloBack to the temporary actor created by the ask pattern.
To do so just change the way the message is passed to anotherActor.
def receive: Actor.Receive = {
case Hallo => anotherActor.tell(Hello, sender)
}

Related

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))

Create child actors based on the request pattern

I am trying to create a web socket server using Play Framework where response from server should be synchronous or asynchronous based on request.
The request will be processed in Parent actor .Based on the action in the request, child actor will be created and message will be passed to child actor for processing and response will be sent back to the controller.
There are predefined actions and sample request for some actions are as follows,
[,,]
["1234","Boot","{"system":"ABCD"}"]
["5678","Start","{"system":"EFGH", "currenTime":"1559548762638"}"]
#Singleton
class RequestController #Inject()(cc: ControllerComponents)(implicit system: ActorSystem, mat: Materializer) extends AbstractController(cc) {
def ws = WebSocket.accept[String, String] {req =>
ActorFlow.actorRef { out =>
ParentActor.props(out)
}
}
}
object ParentActor {
def props(out: ActorRef) = Props(new ParentActor(out))
}
class ParentActor(out : ActorRef) extends Actor {
override def receive: Receive = {
case msg: String =>
//String split opeartion to find the action.
//create child actor for the action and pass the message to the child actor
val action = msg.split(",")[2]
if("Boot".equals(action)){
val bootActor: ActorRef = actorSystem.actorOf(Props[BootActor])
childActor ! msg
}else if("Start".equals(action)){
val startActor: ActorRef = actorSystem.actorOf(Props[StartActor])
startActor ! msg
}
case msg: Response => out ! msg
}
}
case class Response(name:String, msg:String)
class BootActor extends Actor{
override def receive: Receive = {
case msg : String =>
sender() ! Response("ABC",msg)
}
}
class StartActor extends Actor{
override def receive: Receive = {
case msg : String =>
sender() ! Response("Efgh",msg)
}
}
Right now i am getting the action from the request and create a child actor for the action and pass the message to the child actor for processing.
But i am not sure is there any better way or design pattern to process the request and create a child actor instead of String operation?
First of all, there appears to be a typo in your code:
if ("Boot".equals(action)) {
val bootActor: ActorRef = actorSystem.actorOf(Props[BootActor])
childActor ! msg
} else if ("Start".equals(action)) {
val startActor: ActorRef = actorSystem.actorOf(Props[StartActor])
startActor ! msg
}
The message in the first conditional clause should be sent to bootActor instead of childActor, which is undefined in your code snippet.
Another issue is that you're using actorSystem.actorOf to create the child actors. This method creates "top-level" actors, which should be kept to a minimum. Actors created with actorSystem.actorOf are under the supervision of the guardian actor. What this means in relation to your code is that when ParentActor is stopped (i.e., when a WebSocket is closed, Play stops the actor used in ActorFlow, as documented here), the multiple instances of BootActor and StartActor will not be stopped, leaving you with a bunch of idle top-level actors. The remedy is to use context.actorOf to create instances of BootActor and StartActor: doing so makes these instances children of ParentActor.
Also, you should use the == operator instead of the equals method.
Here are the aforementioned changes:
if ("Boot" == action) {
val bootActor: ActorRef = context.actorOf(Props[BootActor])
bootActor ! msg
} else if ("Start" == action) {
val startActor: ActorRef = context.actorOf(Props[StartActor])
startActor ! msg
}
The above could be slightly simplified to the following:
val childActor =
if (action == "Boot") context.actorOf(Props[BootActor])
else context.actorOf(Props[StartActor])
childActor ! msg
To further simplify your code, don't create child actors, which in this case aren't necessary. Move all the logic of interacting with the out actor into a single actor.

Actor supervised by BackoffSupervisor loses stashed messages after restart

I have an actor with stash usage. Sometimes, when it crashes, it loses all stashed messages. I found that it depends on what supervision logic I use.
I wrote a simple example.
An actor with the stash:
case object WrongMessage
case object TestMessage
case object InitialMessage
class TestActor extends Actor with Stash {
override def receive: Receive = uninitializedReceive
def uninitializedReceive: Receive = {
case TestMessage =>
println(s"stash test message")
stash()
case WrongMessage =>
println(s"wrong message")
throw new Throwable("wrong message")
case InitialMessage =>
println(s"initial message")
context.become(initializedReceive)
unstashAll()
}
def initializedReceive: Receive = {
case TestMessage =>
println(s"test message")
}
}
In the following code, TestActor never receives stashed TestMessage:
object Test1 extends App {
implicit val system: ActorSystem = ActorSystem()
val actorRef = system.actorOf(BackoffSupervisor.props(Backoff.onFailure(
Props[TestActor], "TestActor", 1 seconds, 1 seconds, 0
).withSupervisorStrategy(OneForOneStrategy()({
case _ => SupervisorStrategy.Restart
}))))
actorRef ! TestMessage
Thread.sleep(5000L)
actorRef ! WrongMessage
Thread.sleep(5000L)
actorRef ! InitialMessage
}
But this code works well:
class SupervisionActor extends Actor {
val testActorRef: ActorRef = context.actorOf(Props[TestActor])
override def supervisorStrategy: SupervisorStrategy = OneForOneStrategy()({
case _ => SupervisorStrategy.Restart
})
override def receive: Receive = {
case message => testActorRef forward message
}
}
object Test2 extends App {
implicit val system: ActorSystem = ActorSystem()
val actorRef = system.actorOf(Props(classOf[SupervisionActor]))
actorRef ! TestMessage
Thread.sleep(5000L)
actorRef ! WrongMessage
Thread.sleep(5000L)
actorRef ! InitialMessage
}
I looked into sources and found that actor supervision uses
LocalActorRef.restart method which backed by system dispatcher logic, but BackoffSupervisor simply creates a new actor after termination of the old one. Is there any way to work around it?
I'm not sure one can make restart under BackoffSupervisor properly send stashed messages without some custom re-implementation effort.
As you've already pointed out that BackoffSupervisor does its own restart that bypasses the standard actor lifecycle. In fact, it's explicitly noted in the BackoffOnRestartSupervisor source code:
Whatever the final Directive is, we will translate all Restarts to our
own Restarts, which involves stopping the child.
In case you haven't read about this reported issue, it has a relevant discussion re: problem with Backoff.onFailure.
Backoff.onStop would also give the wanted BackoffSupervisor feature, but unfortunately it has its own use cases and won't be triggered by an exception.

Akka actoreRef with macWire DI, actoreRef is not set

I have a 2 actors, Actor1 and Actor2. Actor1 wants to send MyMsg1 to Actor2, and Actor2 after doing some work and getting Future[MyMsg2] wants to send MyMsg2 to Actor1. I have got this working one way but it fails with DI.
Scenario 1 - Working scenario
Actor1 -->MyMsg1-->Actor2
Actor2 MyMsgHandler - Processes message(with Future), does pipeTo to sender with MyMsg2. Works fine, Actor1 recvs MyMsg2
Scenario2 - Failing scenario
Actor1 has a bean injected via MacWire - myBean.
myBean has Actor2 injected as a bean and sends MyMsg1 to Actor2
Actor2 MyMsgHandler processes message(with Future), does pipeTo to sender and tries sending MyMsg2 - Goes to deadLetter. The actor Ref for sender is never set.
How do I fix this?
Pasting code also
class Actor1(failedService: FailedService, actor2: ActorRef ## Actor2) extends Actor{
override def receive: Receive = {
case TriggerActor1() =>
println("Actor1 triggered from REST controller. Send msg to actor 2")
failedService.testSend()
//actor2 ! Msg1()
case Msg2() => println("got msg2 from actor 1")
}
class Actor2 extends Actor {
override def receive: Receive = {
case Msg1() => {
println("send without future")
val origsender = sender()
origsender ! Msg2()
}
}
class FailedService(actor2: ActorRef##Actor2) {
def testSend() = {
actor2 ! Msg1()
}
}
With my current code as shared above, Actor1 is able to send Msg1 to Actor2
and actor2 responds with Msg2 but Msg2 goes to deadletter. I get the below error
akka.actor.DeadLetterActorRef - Message [backup.failedakka.Msg2] from Actor[akka://application/user/actor2#-662746578] to Actor[akka://application/deadLetters] was not delivered. [1] dead letters encountered.
However, if insted of using the line failedService.testSend() in my Actor1, I uncomment the line below it
and use that to communicate, things work fine.
Is the Q clear now? I am injecting dependencies with MacWire
! is defined as:
def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit
You can see the problem now, there's no implicit sender in scope of FailedService, although it is in scope "inside" an actor implementation.
I think you want to do:
class FailedService(actor2: ActorRef##Actor2) {
def testSend(implicit sender: ActorRef) = {
actor2 ! Msg1()
}
}

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
}
}