While refactoring actor codes written by some other programmers, I encountered the usage of Future.onComplete callback within actor A, which goes against the best practice of using akka.pattern.pipe. It's a bad idea since it exposes the possibility of race conditions as the Future instance might be executed on a different thread.
Looking at the code, we see that there are neither sender nor any mutable vars being referred within the onComplete block so it seems pretty safe, at least for this specific occasion. However, one grey area that leaves me wondering are the references to url and especially text.
Is it possible that similar to the Closing Over An Akka Actor Sender In The Receive problem, a race condition happens such that at the time when the onComplete callback is invoked, the value of text already refers to a different actor message, causing all hells to break loose?
class B extends akka.actor.Actor {
def receive = {
case urlAndText: (String, String) => // do something
}
}
class A extends akka.actor.Actor {
case class Insert(url: String)
def fileUpload(content: String): String = ??? // returns the url of the uploaded content
val b = context.actorOf(Props(classOf[B]))
def receive = {
case text: String =>
Future {
fileUpload(text)
} onComplete {
case Success(url) =>
b ! Insert(url, text) // will this be
}
}
}
The reference to text should be fine. The difference is that each "instance" of text is a new variable bound to the scope of the current match block (starting at case text ...). Thus the Future created closes over the value of text just fine.
This is different from sender (or sender() when de-sugared) which is actually a method defined on the Actor trait which returns the ActorRef of the sender of the most recent message received by the actor on which it is called, and so can give a different value when called later (when the Future's onComplete is finally called).
You are right to be suspect about the use of onComplete, too. A better option would be:
case text: String =>
Future {
fileUpload(text)
} map { url =>
Insert(url, text)
} pipeTo b
Now failures will also be sent on to b, rather than being quietly swallowed.
In this case you aren't going to run into any race conditions although as you noted it is not a particularly good idea to structure code this way in general.
The references to both url and text are fine. The value for url is simply an extraction of a successfully completed Future and that doesn't change whether you're in an Actor or not. The value for text is an immutable string and closing over that value in a Future shouldn't cause problems because that string instance is immutable.
As you noted closing over sender or a var is a problem and that's because being mutable objects those values could change by the time the Future completes unlike immutable values which will remain constant even when you close over them.
Related
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).
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.
Trying to understand if the following use of sender are safe.
I saw on some posts that closing over the sender is unsafe but if the call is by value, the sender() is evaluated when the method is called, correct?
are the following patterns safe?
1.
context.system.scheduler.scheduleOnce(1 second, self, "foo")(executor = context.dispatcher, sender = sender())
2.
def receive: Receive = {
case "Process" => context.become(processing(sender())
}
def processing(replyTo: ActorRef): receive = {...}
3.
def receive: Receive = {
case "Process" =>
context.actorOf(Props(new FooActor(replyTo = sender())))
}
When people say "closing" over the sender() function, they mean capturing the "this" reference into a lambda so that sender() will be called at a later time. This is not safe, because the value returned by sender() at a later time will not be the same as the value it currently has. If you capture (or "close over") the value currently returned by sender(), that is fine.
It is not always clear whether you are capturing sender() as a function or sender() as a value, because it will vary depending on whether you pass it to a call-by-value or a call-by-name function, as you have pointed out.
As Viktor points out in his comment:
1 and 2 are fine, because you are capturing the current value of sender().
3 is not safe, because "Props.apply" is a pass-by-name function, so you have inadvertently closed over sender() as a function rather than as a value.
I am not aware of any easy way to distinguish between these two cases, other than checking the docs (or clicking through in your IDE, if it supports it).
In my project I'm using Akka's Actors. By definition Actors are thread-safe, which means that in the Actor's receive method
def receive = {
case msg =>
// some logic here
}
only one thread at a time processes the commented piece of code. However, things are starting to get more complicated when this code is asynchronous:
def receive = {
case msg =>
Future {
// some logic here
}
}
If I understand this correctly, in this case only the Future construct will be synchronized, so to speak, and not the logic inside the Future.
Of course I may block the Future:
def receive = {
case msg =>
val future = Future {
// some logic here
}
Await.result(future, 10.seconds)
}
which solves the problem, but I think we all should agree that this is hardly an acceptable solution.
So this is my question: how can I retain the thread-safe nature of actors in case of asynchronous computing without blocking Scala's Futures?
How can I retain the thread-safe nature of actors in case of
asynchronous computing without block Scalas Future?
This assumption is only true if you modify the internal state of the actor inside the Future which seems to be a design smell in the first place. Use the future for computation only by creating a copy of the data and pipe to result of the computation to the actor using pipeTo. Once the actor receives the result of the computation you can safely operate on it:
import akka.pattern.pipe
case class ComputationResult(s: String)
def receive = {
case ComputationResult(s) => // modify internal state here
case msg =>
Future {
// Compute here, don't modify state
ComputationResult("finished computing")
}.pipeTo(self)
}
I think you need to "resolve" the db query first and then use the result to return a new Future. If the db query returns a Future[A], then you can use flatMap to operate over A and return a new Future. Something in the lines of
def receive = {
case msg =>
val futureResult: Future[Result] = ...
futureResult.flatMap { result: Result =>
// ....
// return a new Future
}
}
the simplest solution here is to turn the actor into a state machine (use AkkaFSM) and do the following:
dispatch a future for the mongoDB request.
use the reference to your own actor to commuincate with your actor
tell the message back from the future.
depending on context you might have to do some more to get a proper response.
But this has the advantage that you process the message with the actor state and you can mutate the actor state as you please as you own the thread.
I am working my way through a book on Scala Actors, and I am running into a bit of a syntactical hangup. In practice, I tend to assign my variables and function definitions as such:
val v: String = "blahblahblah"
def f(n: Int): Int = n+1
including the (return)type of the item after its name. While I know this is not necessary, I have grown comfortable with this convention and find that it makes the code more easily understood by myself.
That being said, observe the below example:
class Server extends Actor {
def act() = {
while (true) {
receive {
case Message(string) => reply("Good,very good.")
}
}
}
}
def sendMsg(m: Message, s: Server): Future[String] = {
s !! m
}
The above code produces an error at compile time, complaining that the server returned a Future[Any], as opposed to a Future[String]. I understand that this problem can be circumvented by removing the return type from sendMsg:
def sendMsg(m: Message,s: Server) = s !! m
However, this is not consistant with my style. Is there a way that I can specify the type of Future that the server generates (as opposed to Future[Any])?
Your problem is a lot deeper than just style: you get a Future[Any] because the compiler cannot statically know better—with the current Akka actors as well as with the now deprecated scala.actors. In the absence of compile-time checks you need to resort to runtime checks instead, as idonnie already commented:
(actorRef ? m).mapTo[String]
This will chain another Future to the original one which is filled either with a String result, a ClassCastException if the actor was naughty, or with a TimeoutException if the actor did not reply, see the Akka docs.
There might be a way out soon, I’m working on an Akka extension to include statically typed channels, but that will lead to you having to write your code a little differently, with more type annotations.