Example
import akka.actor.typed.scaladsl.AskPattern._
object MyActor {
sealed trait Command
case class MyCommand(replyTo: ActorRef[Try[String]]) extends Command
def apply(): Behaviors.Receive[Command] = {
val failureMessage = util.Failure(new RuntimeException("Timeout"))
Behaviors.receiveMessage {
case MyCommand(replyTo) => replyTo ! failureMessage
Behaviors.same
}
}
}
val worker = testKit.spawn(MyActor())
// Act
val res = worker.ask[Try[String]](me => MyActor.MyCommand(replyTo = me))
// Assert
println(Await.result(res, Duration.Inf)) // Return Failure(java.lang.RuntimeException: Timeout)
// But i want to do:
intercept[RuntimeException] {
Await.result(res, Duration.Inf)
}
// and on success i want to return instead of Success[T] as T
}
Notice that I'm wrapping the result with Try
in order to return Success
or Failure
. The problem is that I don't want to do it, i want to fail the whole Future
on the ask
requests.
Is it possible to do it?
CodePudding user response:
For an ask where the response will be a success response or a failure response, in Akka Typed this is best accomplished by using askWithStatus
and adapting the protocol to use StatusReply
:
import akka.pattern.StatusReply
object MyActor {
sealed trait Command
case class MyCommand(replyTo: ActorRef[StatusReply[String]]) extends Command
// can also wrap a Throwable, but it's generally advised to wrap a string
val failure = StatusReply.Error("Timeout")
def apply(): Behavior[Command] =
Behaviors.receiveMessage {
case MyCommand(replyTo) =>
replyTo ! failure
Behaviors.same
}
}
And then, to perform the ask:
val res: Future[String] = worker.askWithStatus[String](MyActor.MyCommand(_))
res
will:
- complete with
string
if the actor being asked replied with aStatusReply.Success(string)
- fail with a
StatusReply.ErrorMessage(errorString)
if the actor being asked replied with aStatusReply.Error(errorString)
- fail with an
x
if the actor being asked replied with aStatusReply.Error(x: Throwable)
- fail with the usual exception a normal
ask
would fail with if the timeout is reached before a response was received
As far as why it's advised to wrap a String error message it's because it's generally the case that the code requesting an actor to do something shouldn't know enough about the internals of the actor for any of the parts of the exception beyond the exception message and (maybe) the type of the exception to be at all meaningful. If you wouldn't be comfortable exposing that detail (e.g. the stacktrace) to a web or API consumer, you probably shouldn't expose it in your ask response. If you order food in a restaurant and the cook in the kitchen drops the pan with your food on the floor, do you need the waiter (your API to the kitchen) to come back and tell you "the cook dropped your food on the floor" or is it just sufficient for the waiter or manager to come and say "sorry, your order will be delayed, here's a drink on the house (or a gift certificate or whatever)"?