Home > Enterprise >  Fail Ask Request on Typed Actor instead wrap with Try
Fail Ask Request on Typed Actor instead wrap with Try

Time:12-23

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 a StatusReply.Success(string)
  • fail with a StatusReply.ErrorMessage(errorString) if the actor being asked replied with a StatusReply.Error(errorString)
  • fail with an x if the actor being asked replied with a StatusReply.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)"?

  • Related