Async operations with Spray, Akka, and actorSelection - scala

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)
}

Related

Appropriate Future Handling in Akka Actors Typed

What is the proper way to handle Futures from inside an Akka (typed) Actor?
For example, assume there is an Actor OrderActor that receives Commands to place orders... which it does by making an http call to an external service. Since these are http calls to an external service, Futures are involved. So, what is the right way to handle that Future from within the Actor.
I read something about the pipeTo pattern. Is that what needs to happen here or something else?
class OrderActor(context: ActorContext[OrderCommand], orderFacade: OrderFacade)
extends AbstractBehavior[OrderCommand](context) {
context.log.info("Order Actor started")
override def onMessage(msg: OrderCommand): Behavior[OrderCommand] = {
msg match {
case PlaceOrder(
referenceId: OrderReferenceId,
ticker: Ticker,
quantity: Int,
replyTo: ActorRef[OrderResult]
) =>
orderFacade
.placeOrder(ticker, quantity) //this returns a Future
.map(res => {
//transform result
//book keeping / notification (affects state)
replyTo ! transformed
//Can/Should we map like this? I tried adding a log statement in here, but I never see it... and the replyTo doesnt seem to get the message.
})
this
It's generally best to avoid doing Future transformations (map, flatMap, foreach, etc.) inside an actor. There's a distinct risk that some mutable state within the actor isn't what you expect it to be when the transformation runs. In Akka Classic, perhaps the most pernicious form of this would result in sending a reply to the wrong actor.
Akka Typed (especially in the functional API) reduces a lot of the mutable state which could cause trouble, but it's still generally a good idea to pipe the Future as a message to the actor.
So if orderFacade.placeOrder results in a Future[OrderResponse], you might add subclasses of OrderCommand like this
// also include fields from the PlaceOrder which will be useful
case class OrderResponseIs(resp: OrderResponse, replyTo: ActorRef[OrderResult]) extends OrderCommand
// TODO include fields
case class OrderFailed() extends OrderCommand
And then pipe the Future to yourself with:
import scala.util.{ Failure, Success }
context.pipeToSelf(orderFacade.placeOrder) {
case Success(resp) => OrderResponseIs(resp, replyTo)
case Failure(_) => OrderFailed()
}
You then have to handle those messages:
case OrderResponseIs(resp, replyTo) =>
// transform resp
val transformed = ???
replyTo ! transformed
this
case OrderFailed() =>
context.log.warning("Stuff is broken")
this
There's not actually much overhead in doing this versus map and friends (both will typically involve scheduling a task for asynchronous execution on the dispatcher).

Which sender is captured inside Actor message handler?

I'm a novice to Akka actors, and I did some experiment as below to learn how actors work.
class RequestActor extends Actor with ActorLogging {
import RequestActor._
implicit val timeout = Timeout(15.seconds)
implicit val ec: ExecutionContext = context.dispatcher
override def receive: Receive = {
case Request(name) =>
// self ? TempRequest(TempRequest(name), xxx) pipeTo sender // Q1: if we reference `sender` here, which `sender` will be captured?
self ? TempRequest(name, sender) onComplete {
// val whichSender = sender // Q2: if we reference `sender` here, which `sender` will be captured?
case Success(TempResponse(msg, origin)) => origin ! HelloReply(msg)
case Failure(ex) => ???
}
case TempRequest(name, origin) =>
sender ! TempReply(s"hello, ${name}!", origin)
case _ =>
???
}
}
I understand we should prefer tell to ask. I made up this example trying to clear several confusions:
Q1 - Q2) I know sender is actually context.sender(). As the comment inside the code snippet, which sender will be captured?
My confusion is, will the sender be the right one resulting in the corresponding ask operation? Or could it be the sender of a message coming later?
3) Will actors be able to continue processing new messages while they're waiting for the reply of the ask?
From the experiment, it seems so. Could anyone please confirm this?
Thanks so much!
// self ? TempRequest(TempRequest(name), xxx) pipeTo sender // Q1: if we reference sender here, which sender will be captured?
The sender of message Request. This is not visible in your snippet.
From comment
self ? TempRequest(TempRequest(name), xxx) pipeTo sender. As this is an ask and waiting will occur, before the response is received, another message could be received by the actor, and thus cause the sender to be change. So, isn't it possible that the pipeTo sender causes the response to send to a wrong actor?
pipeTo pattern will register a callback for the future returned by ? or ask pattern. The callback will reference the sender returned by sender() at the time of callback registration, which is done in blocking manner when Request message is processed. Thus the sender() is the sender of Request message.
pipeTo excludes any possibility of actor context leakage and this is one of the reasons it should be used.
// val whichSender = sender // Q2: if we reference sender here, which sender will be captured?
This will actually cause a bug or leakage of actor context outside of an actor and must not be done. It can be done with a local reference like in following code
case Request(name) =>
val requestSender = context.sender()
self ? TempRequest(name) onComplete {
case Success(TempResponse(msg)) => requestSender ! HelloReply(msg)
}
From docs
When using future callbacks, such as onComplete, or map such as thenRun, or thenApply inside actors you need to carefully avoid closing over the containing actor’s reference, i.e. do not call methods or access mutable state on the enclosing actor from within the callback. This would break the actor encapsulation and may introduce synchronization bugs and race conditions because the callback will be scheduled concurrently to the enclosing actor. Unfortunately there is not yet a way to detect these illegal accesses at compile time.
And finally
3) Will actors be able to continue processing new messages while they're waiting for the reply of the ask?
Yes. There is no "waiting" process. It just registers a callback that is executed outside the actor. Thus the problem of possible actor context leakage.
Additionally,
You could use forward that preserves the original messages sender. And your code can be simplified as following
override def receive: Receive = {
case Request(name) =>
self.forward(TempRequest(name))
case TempRequest(name) =>
sender() ! HelloReply(s"hello, ${name}!"))
}

Akka: when is it safe to send a message

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.

Akka-Http: How to TestProbe a request

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
}
})

Akka Future Response to a Sender

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.