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 _ => }
}
Related
When a child actor receives a custom RESTART message, the actor should restart itself.
(The purpose is to reset the actor member variables, reload external state from db, but not clear the actor internal message queue)
To implement the restart, one workaround is the child actor throws a custom exception, and the parent actor configures its OneForOneStrategy to restart the child actor for this specific exception type.
I'm wondering, if there's a more straightforward approach to do the restart?
The purpose is to reset the actor member variables, reload external state from db
I guess, this is probably the biggest issue, because loading external state might take time and also blocking operation, hence result of the operation is or should be Future[] - so while this future loading your actor should ignore all other messages, until state from DB will be received.
I think ActorCell#become method might help you in this case - so you can change receive method to another, which will ignore rest of messages, except message with DB state or data, and then switch back to regular receive.
Please, see code example below:
import akka.actor.Actor
import akka.pattern._
import scala.concurrent.Future
import scala.collection.mutable
// Database API and external state model example
case class DbExternalState()
trait Database {
def loadExternalState: Future[DbExternalState]
}
import RestartActor._
class RestartActor(database: Database) extends Actor {
private var state = ActorState()
private val suspendedMessages = mutable.Queue[Any]()
override def receive: Receive = defaultReceive
private def defaultReceive: Receive = {
case Restart => restartActorStart()
}
/**
* Wait until message with internal state received and ignore all the other messages (put back un queue)
*/
private def suspendedReceive: Receive = {
case ExternalStateLoaded(state) => restartActorFinish(state)
case message => suspendedMessages.enqueue(message)
}
private def restartActorStart(): Unit = {
import context.dispatcher
context.become(suspendedReceive)
database.loadExternalState.map(ExternalStateLoaded) pipeTo self
}
private def restartActorFinish(dbExternalState: DbExternalState): Unit = {
state = ActorState.initial(dbExternalState)
context.become(defaultReceive) // Return to normal message handling flow
suspendedMessages.foreach(message => self ! message)
suspendedMessages.clear()
}
}
object RestartActor {
// Restart
case object Restart
case class ExternalStateLoaded(state: DbExternalState)
case class ActorState(internalState: List[String] = Nil, externalState: DbExternalState = DbExternalState())
object ActorState {
def initial(externalState: DbExternalState): ActorState = ActorState(externalState = externalState)
}
}
Please, let me know suggestions were correct.
I hope this helps!
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 an actor that represent worker for heavy long time job:
class Worker extends Actor{
override def receive: Receive = {
case "doJob" =>
Thread.sleep(999999)
sender ! "JobResult"
}
}
I would have limit job queue and explicitly reject user, if queue is full. What is best practice to implement this logic. Should I use bounding mailboxes or some dispatcher, that monitoring job queue? Something like this:
class Dispatcher(worker:ActorRef) extends Actor{
val MAX_JOBS = 10
var jobs = 0
override def receive: Receive = {
case "newJob" =>
if (jobs >= MAX_JOBS) sender ! "Try later"
jobs+=1
worker ! "doJob"
case "JobResult" =>
jobs-=1
}
}
Also I not sure how to properly handle failures in that case...
I think the best practice is to use a bounded mailbox for the worker actor.
Then you can configure the bounded in a configuration like this:
bounded-mailbox { mailbox-type = "akka.dispatch.BoundedMailbox"
mailbox-capacity = 1000 mailbox-push-timeout-time = 10s }
akka.actor.mailbox.requirements {
"akka.dispatch.BoundedMessageQueueSemantics" = bounded-mailbox
}
You can also specify the type of mailbox you want (from the built-in types) or create your own custom mailbox with its own traits and specification. (which messages are treated first etc..)
I think that in your scenario it is best:
1. To create your own mailbox with its own error mechanism and custom cleanup/bounding limitations. (based on bounded mailbox traits)
3. Attach your dispatcher to the custom mailbox you created through configuration.
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
}
}
Is it possible to bind a set of AKKA actors to a certain context?
E.g. I have three actors and each of them are implementing a state machine. These actors send messages to each other on certain state transitions. Now I want to bind these state to a context, e.g. a user context. So I have a user (represented by an userId) who has a set of actors bound in certain states. As long as this user context exists these actors (or at least their states) must be bound to the user's context. Is this possible per se in AKKA or do I have to persist the state of the different actors user-wise? Or are actors not designed for these use cases?
I'm not 100% sure that I get what you are asking, but I'll take a shot at an answer anyway. The actor system itself is heirarchical. Parent/child relationships exist in the heirarchy and the parent will be the owner (and supervisor) to the children. If you modeled your system with a UserContext actor being the parent to three child actors (your FSM actors) then I suppose the children will be bound to this UserContext actor instance. Consider this simplified example model:
class UserContext extends Actor{
val stateA = context.actorOf(Props[StateA])
val stateB = context.actorOf(Props[StateB])
val stateC = context.actorOf(Props[StateC])
def receive = {
case _ =>
}
}
class StateA extends Actor{
def receive = {
case _ =>
}
}
class StateB extends Actor{
def receive = {
case _ =>
}
}
class StateC extends Actor{
def receive = {
case _ =>
}
}
If you set things up this way, the state children will be started up when this user context instance is created and will also be stopped when the user context instance is stopped. Now all you need is a little code to make sure only one user context per user exists in the system. You could do something like this to assure that:
object UserContext{
def apply(username:String)(implicit system:ActorSystem):ActorRef = {
val ctx = system.actorFor("/user/" + username)
if (ctx.isTerminated){
try{
system.actorOf(Props[UserContext], username)
}
catch{
case InvalidActorNameException(msg) if msg.contains("unique") => apply(username)
}
}
else
ctx
}
}
This factory object makes sure the user context actor for the supplied username is not currently running. If it is, it just returns that ref. If not, it starts it up and binds it to the name of the user for later lookups.
Once you do things like this, just use the factory to lookup the UserContext for a supplied username and then route all messages through it and let it delegate messages to the correct child state actor. This is obviously quite simplified, but I think it might be something similar to what you want.