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)
}
}
Related
I want to supervise child actors using context.watch(childActor) API
I see that when the child actor has an uncaught exception, indeed the receive message "Terminated" is called on the parent.
But since I am creating many children actors, each with different contexts and messages, I don't know which particular child actor (and surrounding context) actually failed.
For example:
class ParentActor extends Actor {
override def receive = {
case "delegateWorkToChildrenActors" => {
(0 to 100) foreach {
i =>
val child = context.actorOf(Props(new ChildActor(i, "child-actor")
context.watch(child)
val message = SomeSortOfComplexMessageWithManyParameters(...)
val res = child ? message
res.onComplete {
x: Try[Any] => x match {
case Failure(exception: Throwable) => // can only get timeout exception here
case Success(value) => // continue as usual
}
}
}
}
case m # Terminated(actor) => {
// a child actor was terminated but what is its context ? i.e. which message did it try to handle ?
}
}
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 1, withinTimeRange = 1 minute) {
case m # _ => {
Stop // Assume that the actor was stopped or crashed etc.
}
}
}
class ChildActor(number : Int) extends Actor {
override def receive: Receive = {
case SomeSortOfComplexMessageWithManyParameters => {
// might of got some sort of exception here...
// the parent which is monitoring it received the "Terminate" message
}
}
}
How can I get the message that the child actor worked upon?
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
I have the following scenario: the parent supervisor actor creates a child for each message using a factory (function) passed in the constructor.
class supervisorActor(childActorMaker: ActorRefFactory => ActorRef)
extends Actor with ActorLogging{
def receive: Receive = {
case "testThis" =>
val childActor = childActorMaker(context)
childActor!"messageForChild"
}
override val supervisorStrategy =
OneForOneStrategy() {
case _ => Stop
}
}
class childActor extends Actor {
def receive:Receive = {
case _ => /** whatever **/
}
}
In the test I override the child Receive to force an exception. My expectation is the child actor to be stopped every time because of the Supervision Strategy i set.
"When the child Actor throws an exception the Supervisor Actor " should " " +
" stop it" in {
val childActorRef = TestActorRef(new childActor() {
override def receive = {
case msg: String => throw new IllegalArgumentException("kaboom")
}
})
watch(childActorRef)
val maker = (_: ActorRefFactory) => childActorRef
val supervisorActorRef = system.actorOf(Props(new supervisorActor(maker)))
supervisorActorRef!"testThis"
expectTerminated(childActorRef, 1.second)
}
I would expect the child actor to be stoped because of the supervisorStrategy Stop.
Instead I get this error:
java.lang.AssertionError: assertion failed: timeout (1 second) during expectMsg: Terminated
Any idea of why is this happening? Thank you
It seems that the childActorRef is not created with supervisorActorRef's context (I mean ActorContext of supervisorActor you created in the test code).
So childActor(childActorRef) is not a child of supervisorActor(supervisorActorRef) in your test code. That's why supervisor strategy of supervisorActor doesn't serve the purpose.
I have two actors one is parent and one is child ,The child actor is responsible for fetching data from MongoDB against an given id and reply back the data to the calling actor which is a parent in my case ,Now i want to apply supervision in my child actor i know how to perform supervision strategy but how to do it in my code that's confusing me
i am catching exception in my try/catch block so that every type of exception will be caught but then i am stuck on the point how to app,y supervision as i don't know exactly what exception my code will throw in future here is my code please help me
ReadOnlyAdminQueryActor.scala(Patent Actor)
class ReadOnlyAdminQueryActor extends Actor{
val log = LoggerFactory.getLogger("controller")
case ReadOnlyAdminReadByID(idList)=>
var RetunedLists = new MutableList[ReadOnlyAdmin]()
RetunedLists= readById(idList)
sender ! RetunedLists //return list of ReadOnlyAdmin objects to the calling actor (matched uuid results)
def readById(idList:MutableList[Int]):MutableList[ReadOnlyAdmin]= {
var connection=MongoFactory.getConnection
var collection=MongoFactory.getCollection(connection, "readOnlyAdmin")
var RetunedList = new MutableList[ReadOnlyAdmin]()
var id:Int=0
var email:String=""
var SecondryEmail:Option[String]=None
var FirstName:String=""
var LastName:String=""
var userStatus:String=""
log.info("readOnlyAdmin query class data method readByID")
for(Id<-idList){
val q=QueryBuilder.start("_id").is(Id)
val cursor=collection.find(q.get)
var obj=new BasicDBObject
try {
while(cursor.hasNext)
{
obj=cursor.next().asInstanceOf[BasicDBObject]
id=obj.getString("_id").toInt
email=obj.getString("Email")
SecondryEmail=Option(obj.getString("SecondryEmail"))
FirstName=obj.getString("FirstName")
LastName=obj.getString("LastName")
userStatus=obj.getString("UserStatus")
val readOnlyAdmin=new ReadOnlyAdmin(id,FirstName, LastName, email, SecondryEmail ,"",UserStatus.withName(userStatus))
RetunedList+=readOnlyAdmin //adding objects in a list
}//end of while
}//end of try
catch
{
case e: Exception => log.error("printStackTrace"+e.printStackTrace)
}
finally{
cursor.close()
MongoFactory.closeConnection(connection)
}
}//end for loop
RetunedList
}
}
ReadOnlyAdminReadMongoActor.scala (Child Actor)
class ReadOnlyAdminReadMongoActor extends Actor{
val log = LoggerFactory.getLogger("controller")
val ReadOnlyAdminQueryActor=context.actorOf(Props[ReadOnlyAdminQueryActor].withDispatcher("akka.actor.readOnlyAdminReadMongoActor-dispatcher"), name = "ReadOnlyAdminQueryActor")
case ReadOnlyAdminReadFromMongoById(readOnlyAdmin,idList)=>
var RetunedLists = new MutableList[ReadOnlyAdmin]()
implicit val timeout = Timeout(10 seconds)//wait for 10 seconds
override val supervisorStrategy: SupervisorStrategy = {
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
case x: Exception => ???
}
}
val future:Future[MutableList[ReadOnlyAdmin]] = ask(ReadOnlyAdminQueryActor,ReadOnlyAdminReadByID(idList)).mapTo[MutableList[ReadOnlyAdmin]]
future.onComplete {
case Success(result)=>
RetunedLists=result
for(a<-RetunedLists)
{
log.info ("id is "+a.getUuid+"First name is "+a.getFirstName
+"Last name is "+a.getLastName+"Email is "+a.getEmail
+"secondry email is "+a.getSecondryEmail+"user status is "+a.getUserStatus)
}
case Failure(e)=>
log.error(" in failure")
log.error("printStackTrace"+e.printStackTrace)
}
object Test extends App{
val system = ActorSystem("TestSystem")
val readOnlyAdmin= new ReadOnlyAdmin
var uuidsList = new MutableList[Int]()
uuidsList+=123
val ReadOnlyAdminReadMongoActor=system.actorOf(Props[ReadOnlyAdminReadMongoActor].withDispatcher("akka.actor.readOnlyAdminReadMongoActor-dispatcher"), name = "ReadOnlyAdminReadMongoActor")
ReadOnlyAdminReadMongoActor ! ReadOnlyAdminReadFromMongoById(readOnlyAdmin,uuidsList)
}
how can i perform supervision in a correct way and how do i came to know which exception will be thrown in my child actor also Fortunately, Java libraries explicitly say what they're going to throw, and Scala libraries almost always throw very little or no even in IDE no information is shown when we hover the mouse on the code
please help me thanks in advance
The supervisor strategy belongs with the Parent, not the Child. The child should throw its exceptions and the parent determines how to handle the failure. In the code below, the Child actor will be restarted 3 times, then terminate:
class Parent extends Actor {
override def preStart(): Unit = {
self ! "Start Child"
}
def receive = {
case "Start Child" => context.actorOf(Props[Child])
}
override def supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 3) {
case ex: Throwable => Restart
}
}
class Child extends Actor {
override def preStart() = {
self ! "Throw"
}
def receive = {
case "Throw" => throw new Exception("Throwing")
}
}
I need an actor to stop one of its children, so that I can possibly create a new actor with same name (UUID ?).
I've got an ActorSystem with one Actor child. And this child creates new actors with context.actorOf and context.watch. When I try to stop one of these using context.stop, I observe that its postStop method is called as expected, but no matter how long I wait (seconds... minutes...), it never sends back the Terminated message to its creator (and watching) actor.
I read this in the AKKA documentation:
Since stopping an actor is asynchronous, you cannot immediately reuse the name of the child you just stopped; this will result in an InvalidActorNameException. Instead, watch the terminating actor and create its replacement in response to the Terminated message which will eventually arrive.
I don't care waiting for normal termination, but I really need actors to eventually terminate when asked to. Am I missing something ? Should I create actors directly from the system instead of from an actor ?
EDIT:
Here is my code :
object MyApp extends App {
def start() = {
val system = ActorSystem("MySystem")
val supervisor = system.actorOf(Supervisor.props(), name = "Supervisor")
}
override def main(args: Array[String]) {
start()
}
}
object Supervisor {
def props(): Props = Props(new Supervisor())
}
case class Supervisor() extends Actor {
private var actor: ActorRef = null
start()
def newActor(name: String): ActorRef = {
try {
actor = context.actorOf(MyActor.props(name), name)
context.watch(actor)
} catch {
case iane: InvalidActorNameException =>
println(name + " not terminated yet.")
null
}
}
def terminateActor() {
if (actor != null) context.stop(actor)
actor = null
}
def start() {
while (true) {
// do something
terminateActor()
newActor("new name possibly same name as a previously terminated one")
Thread.sleep(5000)
}
}
override def receive = {
case Terminated(x) => println("Received termination confirmation: " + x)
case _ => println("Unexpected message.")
}
override def postStop = {
println("Supervisor called postStop().")
}
}
object MyActor {
def props(name: String): Props = Props(new MyActor(name))
}
case class MyActor(name: String) extends Actor {
run()
def run() = {
// do something
}
override def receive = {
case _ => ()
}
override def postStop {
println(name + " called postStop().")
}
}
EDIT²: As mentionned by #DanGetz, one shall not need to call Thread.sleep in an AKKA actor. Here what I needed was a periodical routine. This can be done using the AKKA context scheduler. See: http://doc.akka.io/docs/akka/2.3.3/scala/howto.html#scheduling-periodic-messages . Instead I was blocking the actor in an infinite loop, preventing it to use its asynchronous mecanisms (messages). I changed the title since the problem was actually not involving actor termination.
It's hard to gauge exactly what you want now that the question has changed a bit, but I'm going to take a stab anyway. Below you will find a modified version of your code that shows both periodic scheduling of a task (one that kicks off the child termination process) and also watching a child and only creating a new one with the same name when we are sure the previous one has stopped. If you run the code below, every 5 seconds you should see it kill the child and wait for the termination message before stating a new one with the exact same name. I hope this is what you were looking for:
object Supervisor {
val ChildName = "foo"
def props(): Props = Props(new Supervisor())
case class TerminateChild(name:String)
}
case class Supervisor() extends Actor {
import Supervisor._
import scala.concurrent.duration._
import context._
//Start child upon creation of this actor
newActor(ChildName)
override def preStart = {
//Schedule regular job to run every 5 seconds
context.system.scheduler.schedule(5 seconds, 5 seconds, self, TerminateChild(ChildName))
}
def newActor(name: String): ActorRef = {
val child = context.actorOf(MyActor.props(name), name)
watch(child)
println(s"created child for name $name")
child
}
def terminateActor(name:String) = context.child(ChildName).foreach{ ref =>
println(s"terminating child for name $name")
context stop ref
}
override def receive = {
case TerminateChild(name) =>
terminateActor(name)
case Terminated(x) =>
println("Received termination confirmation: " + x)
newActor(ChildName)
case _ => println("Unexpected message.")
}
override def postStop = {
println("Supervisor called postStop().")
}
}