Response in akka using ask pattern - scala

i trying get response from my actor using ask pattern
in ask response i have List(scala.concurrent.impl.CallbackRunnable#33d40b3)
but i expect get string "1,2"
How i can get expected results ?
this is my code:
class Storage extends Actor {
var map:ListBuffer[List[String]] = new ListBuffer
val logger = Logging(context.system,this)
override def receive = {
case setRequest(url, urlType)=>
map+=List( url, urlType)
logger.info(s"Putting ${url} to storage")
sender() ! Status.Success
case getRequest()=>
if (map.length >=1){
var response= map(0).mkString(",")
logger.info(s"Send ${response}")
map = map.filter(x => x != response)
sender ! response
}
else{
sender()! Status.Failure(new emptyStorage)
}
case getLength()=>
sender()! map.length
}
}
object Main extends App{
implicit val timeout = Timeout(5 seconds)
val system = ActorSystem.create("default-dispatcher", ConfigFactory.load().getConfig("MyDispatcherExample"))
val storage = system.actorOf(Props(new Storage))
storage ! setRequest("1", "2")
val result = Future {storage ? getRequest }
result onComplete{
case Success(result)=> println(result)
case Failure(result)=> println("some error")
}
}

The ask pattern by itself returns a Future which you're wrapping in an additional Future giving us Future[Future[Any]]. There's not need for that:
val result = storage ? getRequest
result onComplete {
case Success(res) => println(res)
case Failure(e) => println(e)
}
Additionally, when you pass data around in Akka, especially generic data types which are subject to type erasure, it is recommended to wrap them in a case class. For example:
case class Response(result: String)
It is also that a Future returned by ask is untyped. It is recommended to use mapTo in order to cast to a typed response:
val result: Future[Response] = (storage ? getRequest).mapTo[Response]

Related

akka- how to ensure all responses of dynamic number of actors are returned to parent actor?

I need to create variable number of actors each time my program starts and then must ensure all responses are return after a period of time. This
link gives a good idea for fixed number of actors but what about dynamic number?
This is my code that creates actor and passes messages to them:
ruleList = ...
val childActorList: Iterable[ActorRef] = ruleList.map(ruleItem =>
context.actorOf(DbActor.props(ruleItem.parameter1, ruleItem.parameter2)))
implicit val timeout = Timeout(10.second)
childActorList.foreach(childActor =>
childActor ? (tempTableName, lastDate)
)
Updated-1
According to #Raman Mishra guides , I updated my code as bellow, this is the code in parent actor:
override val supervisorStrategy: SupervisorStrategy = {
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
case exp: SQLException => //Resume;
throw exp
case exp:AskTimeoutException => throw exp
case other: Exception => throw other
}
}
override def receive: Receive = {
case Start(tempTableName, lastDate) => {
implicit val timeout = Timeout(10.second)
ruleList.foreach { ruleItem =>
val childActor = context.actorOf(DbActor.props(ruleItem._1, query = ruleItem._2))
ask(childActor, (tempTableName, lastDate)).mapTo[Seq[Int]]
onComplete {
lastDate)).mapTo[Seq[Int]] onComplete {
case util.Success(res) => println("done" + res + ruleItem._2)
case util.Failure(exp: AskTimeoutException) => println("Failed query:" + ruleItem._2); throw exp
case other => println(other)
}
}
And in child actor:
case (brokerTableName, lastDate) => {
Logger("Started query by actor" + self.path.name + ':' +
val repo = new Db()
val res = repo.getAggResult(query = (brokerTableName, lastDate))
val resWrapper = res match {
case elem: Future[Any] => elem
case elem:Any => Future(elem)
}
resWrapper pipeTo self
}
case res:List[Map[Any, Any]] => {
// here final result is send to parent actor
repo.insertAggresults(res, aggTableName) pipeTo context.parent
}
Now, whenever I run main app, first, parent actor starts and create child actors and send messages to them using ask method. Child actors do their tasks but the problem here is child actors response never returns back to parent actor and in every run of app, AskTimeoutException occurs. I doubt if the use of onComplete method is correct or not. Any help will be appreciated.
"Updated-2"
I found out the problem is in using context.parent instead of sender(). Also, when I pipe to sender, first part of my result, and the sender ask for second part, the problem is resolved but I don't know what happens here, why Can't I pipe to self and return the final result to parent?
This is the last code:
In parent actor:
override def receive: Receive = {
case Start(tempTableName, lastDate) => {
println("started: called by remote actor")
implicit val timeout = Timeout(5 second)
ruleList.foreach { ruleItem =>
val childActor = context.actorOf(DbActor.props(ruleItem._1, query = ruleItem._2))
ask(childActor, Broker(tempTableName, lastDate)) onComplete {
// (childActor ? Broker(tempTableName, lastDate)).mapTo[Seq[Int]] onComplete {
case util.Success(res: List[Map[Any, Any]]) => (childActor ? res) onComplete {
case util.Success(res: Seq[Any]) => println("Successfull- Num,ber of documents:" + res.length + " " + ruleItem._2)
case util.Failure(exp: AskTimeoutException) => println("Failed for writing - query:" + ruleItem._2); throw exp
}
case util.Failure(exp: AskTimeoutException) => println("Failed for reading - query :" + ruleItem._2); throw exp
case other => println(other)
}
}
}
}
In child actor:
case (brokerTableName, lastDate) => {
Logger("Started query by actor" + self.path.name + ':' +
val repo = new Db()
val res = repo.getAggResult(query = (brokerTableName, lastDate))
val resWrapper = res match {
case elem: Future[Any] => elem
case elem:Any => Future(elem)
}
resWrapper pipeTo sender()
}
case res:List[Map[Any, Any]] => {
// here final result is send to parent actor
repo.insertAggresults(res, aggTableName) pipeTo sender()
}
The reason that replying to sender() works where replying to context.parent does not is that ask creates an temporary actor to handle the response. You need to reply to this temporary actor: the sender (which is different from the parent).
Also it's not clear whether the getAggResult method is blocking. If so this will not help (see here).

How to simplify future result handling in Akka/Futures?

I want to simplify my for comprehension code to make it as simple as possible.
Here is the code
case object Message
class SimpleActor extends Actor {
def receive = {
case Message => sender ! Future { "Hello" }
}
}
object SimpleActor extends App {
val test = ActorSystem("Test")
val sa = test.actorOf(Props[SimpleActor])
implicit val timeout = Timeout(2.seconds)
val fRes = for {
f <- (sa ? Message).asInstanceOf[Future[Future[String]]]
r <- f
} yield r
println {
Await.result(fRes, 5.seconds)
}
}
Is it possible to get rid of this part
.asInstanceOf[Future[Future[String]]]
?
Look at the pipeTo function which is all about passing a Future between Actors. See here on ask patterns.
myFuture pipeTo sender
The return type of your ask will be Future[String] in your case, which as your comment below asks will need the mapTo[String] to actually get the result type to be a String. Thus your for-comp could be ditched and directly called:
val fRes = (sa ? Message).mapTo[String]

Resolving Akka futures from ask in the event of a failure

I am calling an Actor using the ask pattern within a Spray application, and returning the result as the HTTP response. I map failures from the actor to a custom error code.
val authActor = context.actorOf(Props[AuthenticationActor])
callService((authActor ? TokenAuthenticationRequest(token)).mapTo[LoggedInUser]) { user =>
complete(StatusCodes.OK, user)
}
def callService[T](f: => Future[T])(cb: T => RequestContext => Unit) = {
onComplete(f) {
case Success(value: T) => cb(value)
case Failure(ex: ServiceException) => complete(ex.statusCode, ex.errorMessage)
case e => complete(StatusCodes.InternalServerError, "Unable to complete the request. Please try again later.")
//In reality this returns a custom error object.
}
}
This works correctly when the authActor sends a failure, but if the authActor throws an exception, nothing happens until the ask timeout completes. For example:
override def receive: Receive = {
case _ => throw new ServiceException(ErrorCodes.AuthenticationFailed, "No valid session was found for that token")
}
I know that the Akka docs say that
To complete the future with an exception you need send a Failure message to the sender. This is not done automatically when an actor throws an exception while processing a message.
But given that I use asks for a lot of the interface between the Spray routing actors and the service actors, I would rather not wrap the receive part of every child actor with a try/catch. Is there a better way to achieve automatic handling of exceptions in child actors, and immediately resolve the future in the event of an exception?
Edit: this is my current solution. However, it's quite messy to do this for every child actor.
override def receive: Receive = {
case default =>
try {
default match {
case _ => throw new ServiceException("")//Actual code would go here
}
}
catch {
case se: ServiceException =>
logger.error("Service error raised:", se)
sender ! Failure(se)
case ex: Exception =>
sender ! Failure(ex)
throw ex
}
}
That way if it's an expected error (i.e. ServiceException), it's handled by creating a failure. If it's unexpected, it returns a failure immediately so the future is resolved, but then throws the exception so it can still be handled by the SupervisorStrategy.
If you want a way to provide automatic sending of a response back to the sender in case of an unexpected exception, then something like this could work for you:
trait FailurePropatingActor extends Actor{
override def preRestart(reason:Throwable, message:Option[Any]){
super.preRestart(reason, message)
sender() ! Status.Failure(reason)
}
}
We override preRestart and propagate the failure back to the sender as a Status.Failure which will cause an upstream Future to be failed. Also, it's important to call super.preRestart here as that's where child stopping happens. Using this in an actor looks something like this:
case class GetElement(list:List[Int], index:Int)
class MySimpleActor extends FailurePropatingActor {
def receive = {
case GetElement(list, i) =>
val result = list(i)
sender() ! result
}
}
If I was to call an instance of this actor like so:
import akka.pattern.ask
import concurrent.duration._
val system = ActorSystem("test")
import system.dispatcher
implicit val timeout = Timeout(2 seconds)
val ref = system.actorOf(Props[MySimpleActor])
val fut = ref ? GetElement(List(1,2,3), 6)
fut onComplete{
case util.Success(result) =>
println(s"success: $result")
case util.Failure(ex) =>
println(s"FAIL: ${ex.getMessage}")
ex.printStackTrace()
}
Then it would properly hit my Failure block. Now, the code in that base trait works well when Futures are not involved in the actor that is extending that trait, like the simple actor here. But if you use Futures then you need to be careful as exceptions that happen in the Future don't cause restarts in the actor and also, in preRestart, the call to sender() will not return the correct ref because the actor has already moved into the next message. An actor like this shows that issue:
class MyBadFutureUsingActor extends FailurePropatingActor{
import context.dispatcher
def receive = {
case GetElement(list, i) =>
val orig = sender()
val fut = Future{
val result = list(i)
orig ! result
}
}
}
If we were to use this actor in the previous test code, we would always get a timeout in the failure situation. To mitigate that, you need to pipe the results of futures back to the sender like so:
class MyGoodFutureUsingActor extends FailurePropatingActor{
import context.dispatcher
import akka.pattern.pipe
def receive = {
case GetElement(list, i) =>
val fut = Future{
list(i)
}
fut pipeTo sender()
}
}
In this particular case, the actor itself is not restarted because it did not encounter an uncaught exception. Now, if your actor needed to do some additional processing after the future, you can pipe back to self and explicitly fail when you get a Status.Failure:
class MyGoodFutureUsingActor extends FailurePropatingActor{
import context.dispatcher
import akka.pattern.pipe
def receive = {
case GetElement(list, i) =>
val fut = Future{
list(i)
}
fut.to(self, sender())
case d:Double =>
sender() ! d * 2
case Status.Failure(ex) =>
throw ex
}
}
If that behavior becomes common, you can make it available to whatever actors need it like so:
trait StatusFailureHandling{ me:Actor =>
def failureHandling:Receive = {
case Status.Failure(ex) =>
throw ex
}
}
class MyGoodFutureUsingActor extends FailurePropatingActor with StatusFailureHandling{
import context.dispatcher
import akka.pattern.pipe
def receive = myReceive orElse failureHandling
def myReceive:Receive = {
case GetElement(list, i) =>
val fut = Future{
list(i)
}
fut.to(self, sender())
case d:Double =>
sender() ! d * 2
}
}

How to make a method synchronous in scala?

In this method I would like to see the actual response (result.toJson.toString or StatusCodes.InternalServerError.toString) returned instead of empty string. How can I do that?
def process(msgIn : WebSocketMessageIn, service : ActorRef) : String = {
import model.Registration
import model.RegistrationJsonProtocol._
implicit val timeout = Timeout(10 seconds)
msgIn.method.toUpperCase match {
case "POST" =>
log.debug(s"Handing POST message with body ${msgIn.body}")
val registration = msgIn.body.convertTo[Registration]
val future = (service ? PostRegistrationMessage(registration)).mapTo[Registration]
var response = ""
future onComplete {
case Success(result) =>
response = result.toJson.toString
case Failure(e) =>
log.error(s"Error: ${e.toString}")
response = StatusCodes.InternalServerError.toString
}
response
case "PUT" =>
s"Handing PUT message ${msgIn.body}"
}
}
Here is the code snippet that calls the method and sends the response to websocket
case Message(ws, msg, service) =>
log.debug("url {} received msg '{}'", ws.getResourceDescriptor, msg)
val wsMessageIn = msg.parseJson.convertTo[WebSocketMessageIn]
val response = process(wsMessageIn, service)
ws.send(response);
UPDATE 1: Updated to use Await.result(future, 5000 millis) instead of 'future onComplete { ... }'. Here is code snippet that changed. Works now, but just wondering how we will handle failures.
msgIn.method.toUpperCase match {
case "POST" =>
log.debug(s"Handing POST message with body ${msgIn.body}")
val registration = msgIn.body.convertTo[ADSRegistration]
val future = (service ? PostADSRegistrationMessage(registration)).mapTo[ADSRegistration]
val response = Await.result(future, 5000 millis)
response.toJson.toString
You can use Await.result which is blocking. Something like this:
import scala.concurrent.duration._
val result = Await.result(future, atMost = 10.second)
val response = //result processing
Just the same, you could pass the future back and perform the send in the onComplete which would be much more reactive

wrapping an asynchronous process in an akka actor

I'm dealing with a 3rd-party library which provides me with asynchronous method calls like this:
def doSomething1(input:String, callback:String => Any)
def doSomething2(input:Double, callback:String => Any)
The library is running stuff on some thread it creates.
I'd like to wrap an actor around it so I can ask it for junk, but I'm not sure how to get access to the promise so that I can fulfill the request.
The naive approach:
class Wrapper extends Actor {
def receive {
case s:String => doSomething1(s, sender ! _)
case d:Double => doSomething2(d, sender ! _)
}
}
val wrapper = system.actorOf(Props[Wrapper], "wrapper")
Then ask it for results:
(wrapper ? "hello").mapTo[String].foreach(println)
(wrapper ? 123.456).mapTo[String].foreach(println)
But the result never comes back, presumably because the callback isn't coming from the actor it asked.
Is there some way to get access to the promise so the callback can success it?
Please note that I haven't tested this, but is this about what you're looking for?:
class Wrapper extends Actor {
def receive = {
case s : String =>
val response = Promise[String]()
val originator = sender
doSomething1(s, response.success _)
response.future.foreach(originator ! _)
case d : Double =>
val response = Promise[Double]()
val originator = sender
doSomething2(d, response.success _)
response.future.foreach(originator ! _)
}
}