Hi I have two examples below within the receive method of an actor:
First one does not cache the sender actor for piping
val futureV = //Some function call that returns a Future
futureV.pipeTo(sender)
Second one takes the sender in a val first
val currentS=sender
val futureV = //Some function call that returns a Future
futureV.pipeTo(currentS)
My question is which one is the correct way to code and why?
They are the same. The sender is not going to change. pipeTo takes its argument by value, not by name.
Related
I have a simple code, where I want to get only one boolean value, but it is contained in a Future object:
def getValue(arg: String) : Boolean = for {
data <- SomeObject.getData(arg) //SomeObject is in class constructor
value = data.getBooleanValue
} yield value
The problem here is I need to return Future[Boolean] instead of simple boolean. How I could change it to return only raw type?
As soon as you call an API that returns Future you need to chain your code to execute when that Future completes as far as you possibly can. Avoid waiting for Future to complete as long as possible.
So change the method to return Future:
def getValue(arg: String): Future[Boolean] =
SomeObject.getData(arg).map(_.getBooleanValue)
Then rather than calling
nextMethod(getValue(myArg))
Use map on the Future like this:
getValue(myArg).map{ bool =>
nextMethod(bool) // next processing step
}
This will give a new Future that will contain the result of the next step of the processing.
While this does require re-working the calling code, Scala makes this kind of code very easy to write. Keep chaining operations onto the previous Future until the processing is complete.
In most cases the code will end up with a side-effect operation such as sending a message or writing a file, and in this case there is no need to wait for the Future to complete.
On the rare occasions when you do need to wait for a Future, use Await.result to recover the final result of the chain of Futures
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).
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.
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.