What will happen, when I schedule a message in the constructor of my Actor and the actor fails (Exception) before the message was send?
When the actor is resumed or restarted, will the message send like nothing has happened?
When the actor is stopped, will the message send to the dead letter box?
When I start the timer in preStart(), will I have two scheduled message when the actor restarts after a failure?
The answers to your questions are as follows:
1) Yes, the actor will receive the message as long as you used the scheduler scheduleOnce variant that takes an ActorRef as an arg. Because an ActorRef is just a lightweight proxy based on an actor address, it can survive failures of the target actor and still route messages to it as long as it successfully restarts back up and re-binds to the address that the ActorRef represents/
2) Yes, if the ActorRef is for a path that is no longer represented in the ActorSystem then the message will be sent to deadletter instead.
3) Yes you will. If you do it in preStart or in the body of the actor (constructor) and the actor fails and restarts, then the scheduled will now have two jobs to do for the same ActorRef and thus two requests will eventually be received.
A little code to show all of this in action. Consider the following actor:
class TestSchedActor extends Actor{
import context._
override def preStart = {
context.system.scheduler.scheduleOnce(1 second, self, "bar")
}
def receive = {
case "foo" =>
val s:String = null
s.length
case "baz" =>
context stop self
case other =>
println(s"Got $other")
}
}
If you tested it in this way:
val system = ActorSystem("schedtest")
val ref = system.actorOf(Props[TestSchedActor])
ref ! "foo"
Then the output would be:
[ERROR] [04/03/2014 07:58:24.988] [schedtest-akka.actor.default-dispatcher-2] [ akka://schedtest/user/$a] null
java.lang.NullPointerException
at code.TestSchedActor$$anonfun$receive$1.applyOrElse(Asking.scala:27)
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:498)
at akka.actor.ActorCell.invoke(ActorCell.scala:456)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:237)
at akka.dispatch.Mailbox.run(Mailbox.scala:219)
at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:386)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:262)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:975)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1478)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104)
Got bar
Got bar
This shows both #1 and #3 as the actor still received the message after failure and actually received 2 as it re-scheduled again when it was restarted.
If you tested the actor like this:
val system = ActorSystem("schedtest")
val ref = system.actorOf(Props[TestSchedActor])
ref ! "baz"
Then you would see the following output:
[INFO] [04/03/2014 08:01:14.199] [schedtest-akka.actor.default-dispatcher-2] [akka://schedtest/user/$a] Message [java.lang.String] from
Actor[akka://schedtest/user/$a#687201705] to Actor[akka://schedtest/user/$a#687201705] 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'.
Provided you had not disabled deadletter logging.
I assume that your actor sends a message (using a scheduled task) to itself (using something like system.actorSelection to resolve self address).
Then:
1) Yes;
2) Yes;
3) Yes (moreover, if you start the timer in the constructor you'll get the same behavior).
To avoid all such issues you can start the timer in preStart(), save the received Cancellable into a local variable inside the Actor and then cancel it in postStop(). postStop() / preStart() are called from preRestart() / postRestart(), so your scheduled task will be rescheduled on Actor restarts and cancelled on Actor stop.
Related
I'd like an actor to send a message on startup and receive a reply later.
Sending the message from within preStart results in a temporary sender reference (because the Actor hasn't yet started?). So the reply will likely be a dead letter.
Any tips would be appreciated. Apologies if my premise is mis-informed - I am new to Akka.
One approach is to send a message to self in preStart:
class MyActor extends Actor {
def preStart(): Unit = {
self ! CallService
}
def receive = {
case CallService =>
(service ? ServiceRequest).mapTo[ServiceResponse].pipeTo(self)
case ServiceResponse =>
// do something with the response
}
}
As described in this answer, if you want the actor to send the message before it processes all other messages, then you could stash the other messages:
class MyActor extends Actor with Stash {
def preStart(): Unit = {
self ! CallService
}
def uninitialized: Receive = {
case CallService =>
(service ? ServiceRequest).mapTo[ServiceResponse].pipeTo(self)
unstashAll()
context.become(initialized)
case _ => stash() // if we get a message other than CallService, stash it
}
def initialized: Receive = {
case ServiceResponse =>
// do something with the response from the service
case ...
}
def receive = uninitialized
}
Your premise is indeed not correct: when preStart runs the actor is already fully started, it's self reference never is a temporary one. Without code it is impossible to help you further, though.
The sender should always be considered "temporary" -- cf. this blog post, for example:
The rule is simply never close over the sender method in a block of
code that is potentially executed in another thread, such as a
scheduled task or a Future. The trick is to capture the current sender
in a val, as illustrated below...
-- Closing Over An Akka Actor Sender In The Receive
Make a copy of sender, and then later when you are ready to reply, reply to that copy of the actorRef and not to "sender".
I have the following piece of code in my Actor's (I call this Actor MasterActor) receive method:
override def receive: Receive = {
case StopActor(id, actorConfig) =>
log.info(s"Stopping actor with id = $id and config $actorConfig")
stopActor(id, powerPlantCfg).pipeTo(self)
context.become(waitForStop(sender()))
// Other messages... not shown here for simplicity
}
So what I'm doing above is to stop the actor and pipe the result of that which is a Future[Continue] (where Continue is a Monix Ack type) to the Actor that contains the above Receive method. The stopActor looks like this:
private def stopActor(id: Long, cfg: ActorConfig): Future[Ack] = async {
await(fetchActor(id).materialize) match {
case scala.util.Success(actorRef) =>
log.info(s"Stopping Actor with id = $id")
context.watch(actorRef)
context.stop(actorRef)
Continue
case scala.util.Failure(fail) =>
log.error(s"Could not fetch Actor instance for id = $id because of: $fail")
Continue
}
}
I'm doing the context.watch(actorRef) and this is how my waitForStop looks like:
private def waitForStop(source: ActorRef): Receive = {
case Continue =>
source ! Continue
context.become(receive)
case someShit =>
log.error(s"Unexpected message $someShit received while waiting for an actor to be stopped")
}
So I have 2 questions here:
When doing context.become(waitForStop(sender())), I'm closing in on the sender(), so I assume the sender in this case is the ActorRef that contains all this above code which is the MasterActor. Am I correct?
How do I know explicitly that this ActorRef that I'm trying to stop is actually stopped so that I can do a context.unwatch(actorRef) as soon as it is stopped?
Any suggestions?
You can be notified of the stop of an Actor by watching it. You are already familiar with watch:
val kenny = context.actorOf(Props[Kenny], name = "Kenny")
context.watch(kenny)
and then you can wait for a Terminated message. Once you receive it, you can unwatch what you need.
def receive = {
case Terminated(kenny) => println("OMG, they killed Kenny")
case _ => println("Parent received a message")
}
So my reccomendation would be to simply watch, become waiting for terminated, and issue the stop command. But I'm unsure what you are asking exactly, so this cvould be the wrong ans
Blog post example
I hope it is ok to ask this. I am using akka and have two actors, where one is initiated/created fast and the other much slower. The rapidly created one asks the other for something (ask-pattern), and the message is sent to dead letters since the other is not initiated yet. What is the preferred way of making an actor waiting with sending it´s message? I am not so eager to make an actor sleep or something without knowing there is no other way.
I would use the functionality become()/unbecome() Akka provides for Actors. I am assuming in the following code that the slowActor gets created by the fastActor. The trick here is that the fastActor will have two behaviors: one for when the slowActor is getting initiated and the other for when it's ready to do some work. When slowActor is ready, it will send a message to the fastActor to advertise that is able to receive messages. fastActor will be watching slowActor and if it gets terminated, it will change its behavior again. What to do next would be up to your solution.
Here is a mock code as a guide (I have not compiled the code and it might contain some errors):
case object Ready
case object DoWork
case object WorkDone
class FastActor extends Actor with ActorLogging {
val slowActor = context.actorOf(SlowActor.props)
context.watch(slowActor)
def receive = slowActorNotReadyBehavior
def slowActorNotReadyBehavior = {
case DoWork => log.warning("Slow actor in not ready, I am sorry...")
case Ready => context.become(slowActorReadyBehavior)
}
def slowActorReadyBehavior = {
case DoWork => (slowActor ? DoWork).pipeTo(self)
case Terminated(ref) =>
log.error("Slow actor terminated")
context.unbecome()
//... do something with slowActor
}
}
class SlowActor extends Actor {
override def preStart = {
context.parent ! Ready
}
def receive = {
case DoWork =>
//do something
sender ! WorkDone
}
}
Can anyone explain how and when to use ActorIdentity with a good example?
From documents I can find that "There is a built-in Identify message that all Actors will understand and automatically reply to with a ActorIdentity message containing the ActorRef".
Does that statement mean obtained actor say actorSelector have ActorIdentity message wrapped in my actor?
ActorSelection actorSelector = getContext().actorSelection("/A/B/*");
When you send an Identify message to an ActorSelection the actor will respond, if it exists, with an ActorIdentity message.
If the actor exists the ActorIdentity message will contain a Some(actorRef). It is more efficient to send messages to ActorRefs than ActorSelections.
For example (from the manual):
class Follower extends Actor {
val identifyId = 1
context.actorSelection("/user/another") ! Identify(identifyId)
def receive = {
case ActorIdentity(`identifyId`, Some(ref)) =>
context.watch(ref)
context.become(active(ref))
case ActorIdentity(`identifyId`, None) => context.stop(self)
}
def active(another: ActorRef): Actor.Receive = {
case Terminated(`another`) => context.stop(self)
}
}
The section in the manual that covers this is called Identifying Actors via Actor Selection.
I am brand new to Akka but my understanding about the Stop directive is that it is used inside of SupervisorStrategies when the child should be considered permanently out of service, but there is a way to handle the total outage.
If that understanding is correct, then what I would like to do is have some kind of a “backup actor” that should be engaged after the normal/primary child is stopped and used from that point forward as a fallback. For example, say I have a parent actor who has a child actor - Notifier - whose job it is to send emails. If the Notifier truly dies (say, the underlying mail server goes offline), a backup to this actor might be another actor, say, QueueClient, that sends the notification request to a message broker, where the message will be queued up and replayed at a later time.
How can I define such a SupervisorStrategy to have this built in fault tolerance/actor backup inside of it? Please show code examples, its the only way I will learn!
Overriding Supervisor Strategies beyond the default directives is not commonly done, and not really necessary in your case. A solution would be to watch the child actor from the parent, and when the parent finds that the child is stopped, engage the backup actor.
import akka.actor.SupervisorStrategy.Stop
import akka.actor._
class Parent extends Actor {
var child: ActorRef = context.actorOf(Props[DefaultChild])
context.watch(child)
def receive = {
case Terminated(actor) if actor == child =>
child = context.actorOf(Props[BackupChild])
}
override def supervisorStrategy = OneForOneStrategy() {
case ex: IllegalStateException => Stop
}
}
class DefaultChild extends Actor {
def receive = { case _ => throw new IllegalStateException("whatever") }
}
class BackupChild extends Actor {
def receive = { case _ => }
}