sarah w
sarah w

Reputation: 3515

how to use akka.actor.Status.Failure to catch the exception

I have an actor which is responsible for MongoDB CRUD operations it may throw a mongoException i want to caught this exception in the calling code where i am using the ask pattern but i am still getting a TimeOutException which is not required in my case from the docs i have gone over this link

Warning To complete the with an exception you need to send an akka.actor.Status.Failure message to the sender. This is not done automatically when an actor throws an exception while processing a message.

I followed the code snippet given in the docs

here is my code

class test extends Actor {

def receive () {

case GetRecordLists=>
try {  
 //some operations here 
   sender ! resultList
}
catch {
  mongoEX:MongoException=>
  log.error("got mongodb exception",mongoex)
  sender ! akka.actor.Status.Failure(mongoEx)
  throw mongoEx

e:Exception=>
  log.error("got exception",e)
  sender ! akka.actor.Status.Failure(e)
  throw e
}

}

}


class MainClass extends App {

try {
     val future: Future[scala.collection.mutable.Set[String]] = ask(test, GetRecordLists).mapTo[scala.collection.mutable.Set[String]]
     val results = Await.result(future, timeout.duration)
    }
    catch  {
      case e:Exception=>log.error("got the exception in main class ",e)
      throw new Exception(e)
    }
}

here the expected behavior is to caught the MongoException but i am getting

java.util.concurrent.TimeoutException: Futures timed out after [5 seconds]
    at scala.concurrent.impl.Promise$DefaultPromise.ready(Promise.scala:219) ~[scala-library-2.11.1.jar:na]
    at scala.concurrent.impl.Promise$DefaultPromise.result(Promise.scala:223) ~[scala-library-2.11.1.jar:na]
    at scala.concurrent.Await$$anonfun$result$1.apply(package.scala:111) ~[scala-library-2.11.1.jar:na]
    at scala.concurrent.BlockContext$DefaultBlockContext$.blockOn(BlockContext.scala:53) ~[scala-library-2.11.1.jar:na]
    at scala.concurrent.Await$.result(package.scala:111) ~[scala-library-2.11.1.jar:na]
    at MainClass(MainClass.scala:118) [xyz_2.11.jar:0.1.0-SNAPSHOT]

Upvotes: 0

Views: 1009

Answers (1)

Ivan Stanislavciuc
Ivan Stanislavciuc

Reputation: 7275

What you do, looks correct. The only explanation for timeout error is that operation to mongo has not finished yet and 5 seconds are not enough.

Please check a very simplified code that handles success and failure and timeout cases.

import akka.actor.{Actor, ActorSystem, Props}
import akka.actor.Status.Failure
import akka.util.Timeout
import akka.pattern.ask

import concurrent.duration._

class TestActor(body: () => String) extends Actor {
  override def receive: Receive = {
    case msg => try {
      sender() ! body()
    } catch {
      case ex: Throwable =>
        sender() ! Failure(ex)
    }
  }
}

object TestApp extends App {
  val system = ActorSystem("test")
  import system.dispatcher
  implicit val timeout: Timeout = 1.second
  val actorOk = system.actorOf(Props(new TestActor(() => "Hello")))
  val actorNok = system.actorOf(Props(new TestActor(() => sys.error("Boom"))))
  val actorTimeout = system.actorOf(Props(new TestActor(() => {Thread.sleep(3000); ""})))
  (actorOk ? "some message").mapTo[String].onComplete(println)
  (actorNok ? "some message").mapTo[String].onComplete(println)
  (actorTimeout ? "some message").mapTo[String].onComplete(println)
  Thread.sleep(2000)
  system.terminate()
}

Prints

Success(Hello)
Failure(java.lang.RuntimeException: Boom)
Failure(akka.pattern.AskTimeoutException: Ask timed out on [Actor[akka://test/user/$c#-765807830]] after [1000 ms]. Message of type [java.lang.String]. A typical reason for `AskTimeoutException` is that the recipient actor didn't send a reply.)

Upvotes: 0

Related Questions