I have a request whose response depends on an actor reply. I am trying to test it in this way:
val myActor:TestProbe = TestProbe()
val route = new MyRoute() {
override def myServiceActor:ActorRef = {
myActor.ref
}
}.route
"return a query result for GET" in {
Get("/foo") ~> route ~> check {
myActor.expectMsg(ExecuteFoo())
myActor.reply(FOO)
responseEntity shouldEqual toJsonEntity(RequestResult(FOO))
}
}
I get correctly that expectMsg is verified but the reply is asynchronous respect to the responseEntity check. In this case the test fails.
Is there a way to wait for the reply?
You're on the right track using a TestProbe. The key is to separate the combination of running the request and then checking it (which you are doing as 1 step via check) into two explicit steps. First run it, then do any stubbing on the TestProbe and lastly, make your checks. The updated code for your sample would look like this:
val result = Get("/foo") ~> route ~> runRoute
myActor.expectMsg(ExecuteFoo())
myActor.reply(FOO)
check {
responseEntity shouldEqual toJsonEntity(RequestResult(FOO))
}(result)
You can see there that I'm first using runRoute to simple run the route without performing any checks. I assign this value to result as I will need this later to perform any checks. Then, you can safely do your stubbing against the TestProbe. At this point, it's already received the message and is waiting for your calls to verify what message it got and how to respond. Then, when done with that, we can call check, passing an explicit result (the one from runRoute) to that call instead of relying on an implicit.
If you follow this approach, you should be able to properly test routes that call an actor, using a TestProbe to do so.
One way of doing this (which might not be the right one or the only one) is using AutoPilot. Here is an example:
val myProbe = TestProbe()
myProbe.setAutoPilot(new TestActor.AutoPilot {
def run(sender: ActorRef, msg: Any) = msg match {
case ExecuteFoo(_) =>
//do something else if needed
sender ! FOO
TestActor.NoAutoPilot
}
})
Related
I use Akka myActor1 to ask another myActor2 for a future response like below:
val future = myActor2 ? request
val result = Await.result(future, timeout.duration).asInstanceOf[Person]
let's say result is a case class like this:
case class Person(name: String, age: Int)
I don't want to block the process so I choose not to use Await.result.
Thus I have the below code:
val future = (myActor2 ? request).mapTo[Person]
now future is Future[Person] and not a Future[Any].
how can I then extract the person contained inside the future and handle exception?
I tried something with onComplete but Success seem to only accept Int and not Person. I would like something like below:
future onComplete {
case Success(result) => result
case Failure(failure => doSomethingOnFailure(failure)
}
Do you have any idea?
You want the handling of the response to happen by processing another message. If you are okay with considering the response from the other actor a message, it might be enough to simply send the request with tell (!) and add a case to the receive method for the response.
If you need to avoid confusing parallel requests and responses, you might want to use become and Stash to make sure only 1 request can be in flight at a time. Do realize that this limits the throughput of your system, of course.
If you want to somehow 'translate' the response before passing it to receive handler, the pipe pattern might come in handy:
import akka.pattern.pipe
...
val someIdentifier = ???
(myActor2 ? request)
.map(response => MyCommandWith(response, someIdentifier))
.pipeTo(self)
This will collect the result of the ask, map it and then send it as a message to the current actor (self). Be very careful not to close over any actor state in the map function though!
If you want further result or error handling to be done by the original sender of the message, you don't need pipe at all but you could use forward instead of ask.
There is no magic to convert Future[Person] into Person. You have to either call Await on this future to extract the result or make this piece of code return Future[Smth] and delegate extraction of the result to a caller.
I'm fumbling my way through akka-http; I've got a single route that compiles:
val route = get {
pathPrefix("compass") {
path("route") {
parameters(('srclat.as[Double], 'srclon.as[Double],
'destlat.as[Double], 'destlon.as[Double])) {
(srclat, srclon, destlat, destlon) =>
complete(getRoute((LatLong(srclat, srclon),
LatLong(destlat, destlon))))
}
}
}
}
And I've verified that the parameters are being extracted correctly. When I call the method (with valid lat / longs), I'm expecting to receive an array of coordinates representing a (physical) route, but instead I receive a route object with an empty list of coordinates. Here's the signature of the method being run by complete:
// the Route here is a domain object, not an akka-http Route
def getRoute(coordinates: (LatLong, LatLong)):
Future[Option[Route]] = ???
And starting the server itself looks something like this:
val bindingFuture = Http().bindAndHandle(service.route, "0.0.0.0",
port)
I'm using akka and akka-streams 2.5.4 and akka-http 10.0.9, with Circe support from hseeberger (version 1.18.0). If anyone knows what I'm doing wrong here, please let me know...any help would be appreciated!
instead I receive a route object with an empty list of coordinates.
I think the problem is not in the code shown, but somewhere inside the getRoute function.
I have a hunch that you might be making changes to an immutable case class, and returning a previous copy, rather than the updated version?
e.g. code like the following would give the bug you describe:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
trait LatLong
case class Route(steps: List[String]) // for example
def getRoute(coordinates: (LatLong, LatLong)): Future[Option[Route]] = {
val myRoute = new Route(Nil)
val steps = List("one", "two", "three")
myRoute.copy(steps = steps) // BUG HERE, new copy discarded
Future(Some(myRoute))
}
If that doesn't explain things, please could you show more of the getRoute function?
A quick test to narrow things down might be to change getRoute temporarily to return a hardcoded non-empty Route, and check that it comes back OK over HTTP.
This was completely my fault; due to a cut-and-paste error, the source coordinates were being sent in as both source and destination, so the empty Route object was legitimate. Thanks to those who took their time looking into my screw-up!
I am creating an actor via:
system.actorOf(Props(....))
or
system.actorOf(SmallestMailboxPool(instances).props(Props(....))).
I usually block the thread calling system.actorOf till actorSelection works.
Await.result(system.actorSelection("/user/" + name).resolveOne(), timeout.duration)
I am wondering if this is at all needed or I can immediately start using the actorRef and send (tell) messages to the actor/actor pool.
So the question boils down to, if I have an actorRef, does that mean the mailbox is already created or it might so happen that the messages I sent immediately after calling system.actorOf might get dropped?
If you drill down the the implementation of system.actorOf, you see a call to a method names makeChild. Internally, this utilizes a lengthy method on the ActorRefProvider trait (internally using LocalActorRefProvider) called actorOf. This rather lengthy method initializes the child actor. Relevant parts are:
val props2 =
// mailbox and dispatcher defined in deploy should override props
(if (lookupDeploy) deployer.lookup(path) else deploy) match {
case Some(d) ⇒
(d.dispatcher, d.mailbox) match {
case (Deploy.NoDispatcherGiven, Deploy.NoMailboxGiven) ⇒ props
case (dsp, Deploy.NoMailboxGiven) ⇒ props.withDispatcher(dsp)
case (Deploy.NoMailboxGiven, mbx) ⇒ props.withMailbox(mbx)
case (dsp, mbx) ⇒ props.withDispatcher(dsp).withMailbox(mbx)
}
case _ ⇒ props // no deployment config found
}
Or if a Router is explicitly provided:
val routerDispatcher = system.dispatchers.lookup(p.routerConfig.routerDispatcher)
val routerMailbox = system.mailboxes.getMailboxType(routerProps, routerDispatcher.configurator.config)
// routers use context.actorOf() to create the routees, which does not allow us to pass
// these through, but obtain them here for early verification
val routeeDispatcher = system.dispatchers.lookup(p.dispatcher)
val routeeMailbox = system.mailboxes.getMailboxType(routeeProps, routeeDispatcher.configurator.config)
new RoutedActorRef(system, routerProps, routerDispatcher, routerMailbox, routeeProps, supervisor, path).initialize(async)
Which means that once you get back an ActorRef, the mailbox has been initialized and you shouldn't be scared of sending it messages.
If you think about the semantics of what an ActorRef stands for, it would be a bit pointless to provide one with an ActorRef which is partly/not initialized. It would make system guarantees weak and would make the programmer think twice before passing messages, which is the opposite desire of the framework.
I keep running into the same design problem using spray, which is how to find the original context of the Spray http request for a request, after doing some asynchronous (tell) operations in Akka.
I'm using Net-a-Porter actor per request model. It creates a child actor which I specify to handle each request, which is incapsulated by another actor which holds the correct request context.
Let let's call my actor ActorA, which has this receive method on it:
def receive: Receive = {
case v : InputJson =>
val id = createId
val redisList = context.actorOf(Props[RedisListActor])
// At this point, sender is the 'per-request' actor created, which has the HTTP context of the Spray request.
redisList ! ListRequest(id, sender.path.toStringWithoutAddress, v)
This is adding input to a job queue on redis, which is consumed on another server. When this job is completed, the other server adds the result to a Redis PubSub queue which we are subscribed to. When an item comes into this queue, it alerts my ActorA (using context.actorOf).
case kr : KubernetesReply =>
context.system.actorSelection(kr.actorPath) ! TaskResponse("Success", kr.payload, kr.id)
You can see that I am trying to find the original sender through using it's actorPath, but upon the KubernetesReply, I find that path is deadLetters (even though I have not explicitly killed the request actor). I've confirmed it's the correct path (i.e. I can send back the task response from the InputJson handler).
What's the correct pattern for this? How can I find my original actor, and why has it disappeared?
You can put an ActorRef directly in the ListRequest message.
case class ListRequest(id: YourIdType, requestActor: ActorRef, json: InputJson)
def receive: Receive = {
case v : InputJson =>
val id = createId
val redisList = context.actorOf(Props[RedisListActor])
redisList ! ListRequest(id, sender, v)
case kr : KubernetesReply =>
kr.requestActor ! TaskResponse("Success", kr.payload, kr.id)
}
I came across the following SIP:
http://docs.scala-lang.org/sips/pending/spores.html
As I was reading through, I came across this example:
def receive = {
case Request(data) =>
future {
val result = transform(data)
sender ! Response(result)
}
}
There was a description below in that article:
> Capturing sender in the above example is problematic, since it does not return a stable value. It is possible that the future’s body
> is executed at a time when the actor has started processing the next
> Request message which could be originating from a different actor. As
> a result, the Response message of the future might be sent to the
> wrong receiver.
I do not fully understand about this line "Capturing the sender in the above example is problematic...." Isn't that the case where in each request to the Actor (Request(data)) would create a Future block?
The creating of that Future block is synchronous which would mean that the sender reference is known at that time. It is only that the execution of that Future block is somehow scheduled to happen at a later point in time.
Is my understanding correct?
def receive = {
case Request(data) =>
future {
val result = transform(data)
sender ! Response(result)
}
}
Imagine that the line sender ! Response(result) is executed after 300 ms, exactly at the same time as the enclosing actor is handling another message, let's call it M. Because sender is a def, not a val, it's evaluated every time it's used. This means, that inside the future you've got the sender of the M message! You've responded not to the sender of the original message that created the Future, but to some other guy. To mitigate this problem you need to close over the value of the sender() def at the time of creation of the Future. Compare the original code with this:
def receive = {
case Request(data) =>
val client = sender()
future {
val result = transform(data)
client ! Response(result)
}
}
You have remembered the original sender, so everything is correct.
It's of utmost importance to NEVER execute any methods that depend on time (like sender) or change the actor's state in an asynchronous manner. If you need to change the actor's state in response to some asynchronous computation, you should send yourself a message from the future block.