Akka Futures Exceptions - scala

What happens when an actor of a future throws an exception?
According to the Akka documentation at http://doc.akka.io/docs/akka/snapshot/scala/futures.html:
It doesn't matter if an Actor or the dispatcher is completing the
Future, if an Exception is caught the Future will contain it instead
of a valid result. If a Future does contain an Exception, calling
Await.result will cause it to be thrown again so it can be handled
properly.
I am not sure this is what I am seeing when running this piece of code:
class Worker extends Actor {
def receive = {
case i: Int => throw new RuntimeException
}
}
implicit val system = ActorSystem("MySystem")
val worker = system.actorOf(Props(new Worker), name="worker")
implicit val timeout = Timeout(5 minutes)
val future = worker ? 0
val res = Await.result(future, 10 seconds)
According to the documentation, Await.result should throw the exception again, but what I am getting is a TimeoutException! Can someone clarify on this?

For actors you need to catch the exception and return it as a failure status. Right now you're not returning anything to the sender so you're getting a timeout exception:
class Worker extends Actor {
def receive = {
case i: Int => {
try {
throw new RuntimeException
sender ! "Some good result"
} catch {
case e: Exception =>
sender ! akka.actor.Status.Failure(e) // Alert the sender of the failure
throw e // Alert any supervisor actor of the failure
}
}
}
}
Futures can handle this a little more gracefully since they always send a result, while actors do not (this would give you the same result as above):
val future = Future {
throw new RuntimeException
}

Related

Error logging propagation when using Akka Supervision

Here's the thing am kinda stuck at. I have a SupervisorActor which creates Actor A and B and so on. There are no child actors to ActorA or ActorB.Lets say both Actor A and B hit Database and get SQL exception. This is propagated to the SupervisorActor up the chain. When I catch SQL exception, I also need to Log that Actor A had a SQL exception. But how can I achieve this?
1 way I could think is my Actor A logs it and throws an exception above the call stack. But I would need a try-catch block in my code. Which kinda defeats the purpose here.
Creating
Another way, I can think of is Actor A and B create a new child Actor A1 which would send it up the chain but that's not an option coz that's a common library without actors.
Is there a way to achieve something similar to :
Yes! I wanted to know if we can achieve something like:
try{
saveUser()
}
catch {
case b: BatchUpdateException =>
logger.error("We received a BatchUpdateException when trying to save the user")
throw b
}
case e: Exception =>
logger.error("Some other exception occured ")
throw e
}
try{
saveSeller()
}
catch {
case b: BatchUpdateException =>
logger.error("We received a BatchUpdateException when trying to save the Seller details")
throw b
}
case e: Exception =>
logger.error("Some other exception occured ")
throw e
}
PS: Am not sure if supervision strategy is the right approach to achieve what I am trying to achieve. I am trying to explore new possibilities.
Centralizing fault-handling logic within a supervisor actor using SupervisorStrategy is a better approach than scattering/duplicating it across individual actors. In particular, expressing the Exception-handling logic as the decider parameter of type PartialFunction[Throwable, Directive] helps improve code maintainability.
When I catch SQL exception, I also need to Log that Actor A had a SQL exception. But how can I achieve this?
Within the supervisor actor, you can always log Exceptions from individual child actors within the supervisor actor by including the corresponding actor references via sender. Below is a trivialized example of a supervisor actor logging actor-specific Exceptions from a couple of child actors and taking corresponding Resume/Stop/Escalate actions:
import akka.actor.{Actor, ActorSystem, Props, ActorLogging}
import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._
import java.sql.SQLException
implicit val system = ActorSystem("system")
implicit val ec = system.dispatcher
case class CreateWorker(props: Props, name: String)
case class BogusQuery(ex: Exception)
def doQuery(q: BogusQuery) = throw q.ex
class MySupervisor extends Actor with ActorLogging {
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 5, withinTimeRange = 1.minute) {
case e: SQLException =>
log.error(s"Supervisor: $e from $sender; Resuming!")
Resume
case e: NullPointerException =>
log.error(s"Supervisor: $e from $sender; Stopping!")
Stop
case _: Exception =>
log.error(s"Supervisor: Unknown exception from $sender; Escalating!")
Escalate
}
def receive = {
case w: CreateWorker => sender ! context.actorOf(w.props, w.name)
}
}
class MyWorker extends Actor with ActorLogging {
def receive = {
case q: BogusQuery =>
log.info(s"$self: Received '$q'!")
doQuery(q)
case x =>
log.error(s"$self: Unknown value '${x}'!")
}
}
val supervisor = system.actorOf(Props[MySupervisor], "supervisor")
supervisor ! CreateWorker(Props[MyWorker], "workerA")
supervisor ! CreateWorker(Props[MyWorker], "workerB")
val workerA = system.actorSelection("/user/supervisor/workerA")
val workerB = system.actorSelection("/user/supervisor/workerB")
workerA ! BogusQuery(new SQLException)
// [INFO] [<timestamp>] [<dispatcher>] [akka://system/user/supervisor/workerA]
// Actor[akka://system/user/supervisor/workerA#-2129514903]:
// Received 'BogusQuery(java.sql.SQLException)'!
// [ERROR] [<timestamp>] [<dispatcher>] [akka://system/user/supervisor]
// Supervisor: java.sql.SQLException from
// Actor[akka://system/user/supervisor/workerA#-2129514903]; Resuming!
// [WARN] [<timestamp>] [<dispatcher>] [akka://system/user/supervisor/workerA] null
workerB ! BogusQuery(new NullPointerException)
// [ERROR] [<timestamp>] [<dispatcher>] [akka://system/user/supervisor]
// Supervisor: java.lang.NullPointerException from
// Actor[akka://system/user/supervisor/workerB#-1563197689]; Stopping!
// [ERROR] [<timestamp>] [<dispatcher>] [akka://system/user/supervisor/workerB] null
// java.lang.NullPointerException ...

Why i'm not getting Ask timeout exception?

I have 2 actors one supervisor and a child actor.
The supervisor:
class DemoActorSupervisor(implicit val system: ActorSystem, config: Config) extends Actor {
val childActor: ActorRef = context.actorOf(FromConfig.props(Props[DemoActorChild]), "DemoChildActor")
context.watch(childActor)
override def receive: Receive = {
case s: String =>
childActor forward s
}
}
Child actor:
class DemoActorChild extends Actor {
def receive: Receive = {
case s: String =>
Thread.sleep(100)
Future.successful(true) pipeTo (sender)
}
}
Main method:
object ABC extends App {
implicit val system: ActorSystem = ActorSystem("Demo")
implicit val config: Config = ConfigFactory.load()
implicit val timeout: Timeout = Timeout(5, TimeUnit.MILLISECONDS)
val supervisor = system.actorOf(DemoActorSupervisor.props(), "DemoSupervisor")
val x: Future[Boolean] = (supervisor ? ("ASK")).mapTo[Boolean]
x.foreach(println)
}
I have set the ask timeout as 5 mili seconds, and doing an ask call to the supervisor actor. which is forwarding the message to the child actor. In the child actor I have put Thread.sleep(100) logically I should get the ask timeout exception as I have set timeout to 5 mili seconds and child is taking more than 100 mili second to respond back, but I am not getting ask timeout exception.
can someone tell me what's wrong with the code? How can I get ask timeout exception.
You can see in the description of Future.foreach:
Asynchronously processes the value in the future once the value becomes available.
WARNING: Will not be called if this future is never completed or if it is completed with a failure.
If you want to use Future.foreach, you should write something like:
x.map(Success(_)).recover({case exception => Failure(exception)}).foreach(println)
Try next:
x.onComplete {
case Success(v) =>
println(v)
case Failure(v) =>
println(v)
}
Future foreach just handle success case, akka.pattern.AskTimeoutException: belongs to Failure, you need to handle it by your code.
Or next also ok:
x.foreach(println)
x.failed.foreach(println)

Resolving Akka futures from ask in the event of a failure

I am calling an Actor using the ask pattern within a Spray application, and returning the result as the HTTP response. I map failures from the actor to a custom error code.
val authActor = context.actorOf(Props[AuthenticationActor])
callService((authActor ? TokenAuthenticationRequest(token)).mapTo[LoggedInUser]) { user =>
complete(StatusCodes.OK, user)
}
def callService[T](f: => Future[T])(cb: T => RequestContext => Unit) = {
onComplete(f) {
case Success(value: T) => cb(value)
case Failure(ex: ServiceException) => complete(ex.statusCode, ex.errorMessage)
case e => complete(StatusCodes.InternalServerError, "Unable to complete the request. Please try again later.")
//In reality this returns a custom error object.
}
}
This works correctly when the authActor sends a failure, but if the authActor throws an exception, nothing happens until the ask timeout completes. For example:
override def receive: Receive = {
case _ => throw new ServiceException(ErrorCodes.AuthenticationFailed, "No valid session was found for that token")
}
I know that the Akka docs say that
To complete the future with an exception you need send a Failure message to the sender. This is not done automatically when an actor throws an exception while processing a message.
But given that I use asks for a lot of the interface between the Spray routing actors and the service actors, I would rather not wrap the receive part of every child actor with a try/catch. Is there a better way to achieve automatic handling of exceptions in child actors, and immediately resolve the future in the event of an exception?
Edit: this is my current solution. However, it's quite messy to do this for every child actor.
override def receive: Receive = {
case default =>
try {
default match {
case _ => throw new ServiceException("")//Actual code would go here
}
}
catch {
case se: ServiceException =>
logger.error("Service error raised:", se)
sender ! Failure(se)
case ex: Exception =>
sender ! Failure(ex)
throw ex
}
}
That way if it's an expected error (i.e. ServiceException), it's handled by creating a failure. If it's unexpected, it returns a failure immediately so the future is resolved, but then throws the exception so it can still be handled by the SupervisorStrategy.
If you want a way to provide automatic sending of a response back to the sender in case of an unexpected exception, then something like this could work for you:
trait FailurePropatingActor extends Actor{
override def preRestart(reason:Throwable, message:Option[Any]){
super.preRestart(reason, message)
sender() ! Status.Failure(reason)
}
}
We override preRestart and propagate the failure back to the sender as a Status.Failure which will cause an upstream Future to be failed. Also, it's important to call super.preRestart here as that's where child stopping happens. Using this in an actor looks something like this:
case class GetElement(list:List[Int], index:Int)
class MySimpleActor extends FailurePropatingActor {
def receive = {
case GetElement(list, i) =>
val result = list(i)
sender() ! result
}
}
If I was to call an instance of this actor like so:
import akka.pattern.ask
import concurrent.duration._
val system = ActorSystem("test")
import system.dispatcher
implicit val timeout = Timeout(2 seconds)
val ref = system.actorOf(Props[MySimpleActor])
val fut = ref ? GetElement(List(1,2,3), 6)
fut onComplete{
case util.Success(result) =>
println(s"success: $result")
case util.Failure(ex) =>
println(s"FAIL: ${ex.getMessage}")
ex.printStackTrace()
}
Then it would properly hit my Failure block. Now, the code in that base trait works well when Futures are not involved in the actor that is extending that trait, like the simple actor here. But if you use Futures then you need to be careful as exceptions that happen in the Future don't cause restarts in the actor and also, in preRestart, the call to sender() will not return the correct ref because the actor has already moved into the next message. An actor like this shows that issue:
class MyBadFutureUsingActor extends FailurePropatingActor{
import context.dispatcher
def receive = {
case GetElement(list, i) =>
val orig = sender()
val fut = Future{
val result = list(i)
orig ! result
}
}
}
If we were to use this actor in the previous test code, we would always get a timeout in the failure situation. To mitigate that, you need to pipe the results of futures back to the sender like so:
class MyGoodFutureUsingActor extends FailurePropatingActor{
import context.dispatcher
import akka.pattern.pipe
def receive = {
case GetElement(list, i) =>
val fut = Future{
list(i)
}
fut pipeTo sender()
}
}
In this particular case, the actor itself is not restarted because it did not encounter an uncaught exception. Now, if your actor needed to do some additional processing after the future, you can pipe back to self and explicitly fail when you get a Status.Failure:
class MyGoodFutureUsingActor extends FailurePropatingActor{
import context.dispatcher
import akka.pattern.pipe
def receive = {
case GetElement(list, i) =>
val fut = Future{
list(i)
}
fut.to(self, sender())
case d:Double =>
sender() ! d * 2
case Status.Failure(ex) =>
throw ex
}
}
If that behavior becomes common, you can make it available to whatever actors need it like so:
trait StatusFailureHandling{ me:Actor =>
def failureHandling:Receive = {
case Status.Failure(ex) =>
throw ex
}
}
class MyGoodFutureUsingActor extends FailurePropatingActor with StatusFailureHandling{
import context.dispatcher
import akka.pattern.pipe
def receive = myReceive orElse failureHandling
def myReceive:Receive = {
case GetElement(list, i) =>
val fut = Future{
list(i)
}
fut.to(self, sender())
case d:Double =>
sender() ! d * 2
}
}

Akka supervisor actor do not handle exception when child actor throws an exception within onFailure of a future

I'm facing a problem with an Akka supervisor actor. When the child actor throws an exception within onFailure method of a future result, the supervisor does not handle the error (I want to restart the child in the case of a ConnectException).
I'm using Akka 2.3.7.
This is the supervisor actor:
class MobileUsersActor extends Actor with ActorLogging {
import Model.Implicits._
import Model.MobileNotifications
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 3, withinTimeRange = 1 minute) {
case _: java.net.ConnectException => {
Logger.error("API connection error. Check your proxy configuration.")
Restart
}
}
def receive = {
case Start => findMobileUsers
}
private def findMobileUsers = {
val notis = MobileNotificationsRepository().find()
notis.map(invokePushSender)
}
private def invokePushSender(notis: List[MobileNotifications]) = {
notis.foreach { n =>
val pushSender = context.actorOf(PushSenderActor.props)
pushSender ! Send(n)
}
}
}
And this is the child actor:
class PushSenderActor extends Actor with ActorLogging {
def receive = {
case Send(noti) => {
val response = sendPushNotification(noti) onFailure {
case e: ConnectException => throw e
}
}
}
private def sendPushNotification(noti: MobileNotifications): Future[WSResponse] = {
val message = "Push notification message example"
Logger.info(s"Push Notification >> $message to users " + noti.users)
PushClient.sendNotification(message, noti.users)
}
}
I tried to notify sender with an akka.actor.Status.Failure(e) as is suggested here, but did not work, the exception keep unhandled by the supervisor.
As a workaround, I found this way to get it work:
class PushSenderActor extends Actor with ActorLogging {
def receive = {
case Send(noti) => {
val response = sendPushNotification(noti) onFailure {
case e: ConnectException => self ! APIConnectionError
}
}
case APIConnectionError => throw new ConnectException
}
private def sendPushNotification(noti: MobileNotifications): Future[WSResponse] = {
val message = "Push notification message example"
Logger.info(s"Push Notification >> $message to users " + noti.users)
PushClient.sendNotification(message, noti.users)
}
}
Is this an Akka bug or am I doing something wrong?
Thanks!
I think that the problem is that the exception thrown inside the Future doesn't belong to the same thread (potentially) as the one the Actor is running (someone more experienced can elaborate on this). So, the problem is that the exception thrown inside the Future body is "swallowed" and not propagated to the Actor. Since this is the case, the Actor doesn't fail and so there's no need to apply the supervision strategy. So, the first solution that comes to my mind is to wrap the exception inside the Future in some message, send it to yourself, and then throw it from inside the Actor context itself. This time, the Exception will be caught and the supervision strategy will be applied. Note, however, that unless you send the Send(noti) message again, you will not see the Exception happening since the Actor was restarted. All in all, the code would be like this:
class PushSenderActor extends Actor with ActorLogging {
case class SmthFailed(e: Exception)
def receive = {
case Send(noti) => {
val response = sendPushNotification(noti) onFailure {
case e: ConnectException => self ! SmthFailed(e) // send the exception to yourself
}
}
case SmthFailed(e) =>
throw e // this one will be caught by the supervisor
}
private def sendPushNotification(noti: MobileNotifications): Future[WSResponse] = {
val message = "Push notification message example"
Logger.info(s"Push Notification >> $message to users " + noti.users)
PushClient.sendNotification(message, noti.users)
}
}
Hope it helped.

How to handle exception with ask pattern and supervision

How should I handle an exception thrown by the DbActor here ? I'm not sure how to handle it, should pipe the Failure case ?
class RestActor extends Actor with ActorLogging {
import context.dispatcher
val dbActor = context.actorOf(Props[DbActor])
implicit val timeout = Timeout(10 seconds)
override val supervisorStrategy: SupervisorStrategy = {
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
case x: Exception => ???
}
}
def receive = {
case GetRequest(reqCtx, id) => {
// perform db ask
ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete {
case Success(obj) => { // some stuff }
case Failure(err) => err match {
case x: Exception => ???
}
}
}
}
}
Would be glad to get your thought, thanks in advance !
There are a couple of questions I can see here based on the questions in your code sample:
What types of things can I do when I override the default supervisor behavior in the definition of how to handle exceptions?
When using ask, what types of things can I do when I get a Failure result on the Future that I am waiting on?
Let's start with the first question first (usually a good idea). When you override the default supervisor strategy, you gain the ability to change how certain types of unhandled exceptions in the child actor are handled in regards to what to do with that failed child actor. The key word in that previous sentence is unhandled. For actors that are doing request/response, you may actually want to handle (catch) specific exceptions and return certain response types instead (or fail the upstream future, more on that later) as opposed to letting them go unhandled. When an unhandled exception happens, you basically lose the ability to respond to the sender with a description of the issue and the sender will probably then get a TimeoutException instead as their Future will never be completed. Once you figured out what you handle explicitly, then you can consider all the rest of exceptions when defining your custom supervisor strategy. Inside this block here:
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
case x: Exception => ???
}
You get a chance to map an exception type to a failure Directive, which defines how the failure will be handled from a supervision standpoint. The options are:
Stop - Completely stop the child actor and do not send any more messages to it
Resume - Resume the failed child, not restarting it thus keeping its current internal state
Restart - Similar to resume, but in this case, the old instance is thrown away and a new instance is constructed and internal state is reset (preStart)
Escalate - Escalate up the chain to the parent of the supervisor
So let's say that given a SQLException you wanted to resume and given all others you want to restart then your code would look like this:
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
case x: SQLException => Resume
case other => Restart
}
Now for the second question which pertains to what to do when the Future itself returns a Failure response. In this case, I guess it depends on what was supposed to happen as a result of that Future. If the rest actor itself was responsible for completing the http request (let's say that httpCtx has a complete(statusCode:Int, message:String) function on it), then you could do something like this:
ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete {
case Success(obj) => reqCtx.complete(200, "All good!")
case Failure(err:TimeoutException) => reqCtx.complete(500, "Request timed out")
case Failure(ex) => reqCtx.complete(500, ex.getMessage)
}
Now if another actor upstream was responsible for completing the http request and you needed to respond to that actor, you could do something like this:
val origin = sender
ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete {
case Success(obj) => origin ! someResponseObject
case Failure(ex) => origin ! Status.Failure(ex)
}
This approach assumes that in the success block you first want to massage the result object before responding. If you don't want to do that and you want to defer the result handling to the sender then you could just do:
val origin = sender
val fut = ask(dbActor, ReadCommand(reqCtx, id))
fut pipeTo origin
For simpler systems one may want to catch and forward all of the errors. For that I made this small function to wrap the receive method, without bothering with supervision:
import akka.actor.Actor.Receive
import akka.actor.ActorContext
/**
* Meant for wrapping the receive method with try/catch.
* A failed try will result in a reply to sender with the exception.
* #example
* def receive:Receive = honestly {
* case msg => sender ! riskyCalculation(msg)
* }
* ...
* (honestActor ? "some message") onComplete {
* case e:Throwable => ...process error
* case r:_ => ...process result
* }
* #param receive
* #return Actor.Receive
*
* #author Bijou Trouvaille
*/
def honestly(receive: =>Receive)(implicit context: ActorContext):Receive = { case msg =>
try receive(msg) catch { case error:Throwable => context.sender ! error }
}
you can then place it into a package file and import a la akka.pattern.pipe and such. Obviously, this won't deal with exceptions thrown by asynchronous code.