Escalation of exceptions in akka actors and futures in scala - scala

I am developing a server which is an intermediate server between 2 other endpoints.
So I implement a spray route which triggers a computation, and this computation invokes an actor which invokes a spray client.
The following pattern applies:
Client => ...=> (Spray Route => Service Actor => Spray Client) =>... => distant Server.
I am developing the exception management and I have a hard time understanding what to do.
When the distant server sends me BadRequest, or any error code, I want to throw an exception, which leads answering to the client the same kind of errors.
I guess my question is general about exception handling and excalation.
I naively believed that when I throw an exception in a future, the future could call the case Failure:
def receive = {
case SendRequest => {
val s = sender()
val call = for {
request <- ComputeRequest
result <- CallSprayClient ? request
} yield result
call onComplete {
case Success(succ) => s ! succ
case Failure(e) = throw e
}
}
}
The thing is, when ComputeRequest or CallSprayClient throw an exception, the Failure case of my callback is not triggered.
I looked at the supervision pattern, but it seems that the exception or message causing the error is not propagated neither.
In my particular case, depending on the exception, I would like to send a different http response to my client, hence the need to escalate.
What development pattern should I apply?
Thanks.

Related

Expecting a List[Future], what if there is a failed Future?

I have list of IPs like this
val ips = List("ip1", "ip2", "ip3")
I use that list to do http get requests via my wrapper get method, and my wrapper get method will throw BadRequestException if the response status is 400. I understand if I get all success say 200 since myWrapper returns Future[WSResponse], in good case my end result will be List[Future[WSResponse]]
Example snippet
def myresponses(ips:List[String]) = {
ips.map(ip=>myWrapper(ip))
}
My confusion and question is that since myWrapper can throw an exception in case of status 404, will other guys still execute? In short in bad case say where one request gives 404 and say the remaining all give 200, is it going to be the case that I will have List[WSResponse] and for one of the elements it will be an exception? Or it will not be the case and if one Future has failed, the others might not even execute?
They will all execute. If there is an exception, that element will be a failed future. It will still have the type Future[Response], but if you try to Await.result on it, an exception will be thrown. If you try to further transform (map, flatMap, etc.) such failed Future, nothing will happen (the transformation will never be executed).
You can handle those exceptions with .recover or .recoverWith (they are like .map and flatMap, but will only invoke the transformation if the future has failed), or report/clean up it with .onFailure (there is also .onSuccess and .onComplete, that handles both cases).
There is a good tutorial on dealing with scala futures here:
https://docs.scala-lang.org/overviews/core/futures.html

Akka Http - Host Level Client Side API Source.queue pattern

We started to implement the Source.queue[HttpRequest] pattern mentioned in the docs: http://doc.akka.io/docs/akka-http/current/scala/http/client-side/host-level.html#examples
This is the (reduced) example from the documentation
val poolClientFlow = Http()
.cachedHostConnectionPool[Promise[HttpResponse]]("akka.io")
val queue =
Source.queue[(HttpRequest, Promise[HttpResponse])](
QueueSize, OverflowStrategy.dropNew
)
.via(poolClientFlow)
.toMat(Sink.foreach({
case ((Success(resp), p)) => p.success(resp)
case ((Failure(e), p)) => p.failure(e)
}))(Keep.left)
.run()
def queueRequest(request: HttpRequest): Future[HttpResponse] = {
val responsePromise = Promise[HttpResponse]()
queue.offer(request -> responsePromise).flatMap {
case QueueOfferResult.Enqueued => responsePromise.future
case QueueOfferResult.Dropped => Future.failed(new RuntimeException("Queue overflowed. Try again later."))
case QueueOfferResult.Failure(ex) => Future.failed(ex)
case QueueOfferResult.QueueClosed => Future.failed(new RuntimeException("Queue was closed (pool shut down) while running the request. Try again later."))
}
}
val responseFuture: Future[HttpResponse] = queueRequest(HttpRequest(uri = "/"))
The docs state that using Source.single(request) is an anti-pattern and should be avoid. However it doesn't clarify why and what implications come by using Source.queue.
At this place we previously showed an example that used the Source.single(request).via(pool).runWith(Sink.head).
In fact, this is an anti-pattern that doesn’t perform well. Please either supply requests using a queue or in a streamed fashion as shown below.
Advantages of Source.queue
The flow is only materialized once ( probably a performance gain? ). However if I understood the akka-http implementation correctly, a new flow is materialized for each connection, so this doesn't seem to be that much of a problem
Explicit backpressure handling with OverflowStrategy and matching over the QueueOfferResult
Issues with Source.queue
These are the questions that came up, when we started implementing this pattern in our application.
Source.queue is not thread-safe
The queue implementation is not thread safe. When we use the queue in different routes / actors we have this scenario that:
A enqueued request can override the latest enqueued request, thus leading to an unresolved Future.
UPDATE
This issue as been addressed in akka/akka/issues/23081. The queue is in fact thread safe.
Filtering?
What happens when request are being filtered? E.g. when someone changes the implementation
Source.queue[(HttpRequest, Promise[HttpResponse])](
QueueSize, OverflowStrategy.dropNew)
.via(poolClientFlow)
// only successful responses
.filter(_._1.isSuccess)
// failed won't arrive here
.to(Sink.foreach({
case ((Success(resp), p)) => p.success(resp)
case ((Failure(e), p)) => p.failure(e)
}))
Will the Future not resolve? With a single request flow this is straightforward:
Source.single(request).via(poolClientFlow).runWith(Sink.headOption)
QueueSize vs max-open-request?
The difference between the QueueSize and max-open-requests is not clear. In the end, both are buffers. Our implementation ended up using QueueSize == max-open-requests
What's the downside for Source.single()?
Until now I have found two reasons for using Source.queue over Source.single
Performance - materializing the flow only once. However according to this answer it shouldn't be an issue
Explicitly configuring backpressure and handle failure cases. In my opinion the ConnectionPool has a sufficient handling for too much load. One can map over the resulting future and handle the exceptions.
thanks in advance,
Muki
I'll answer each of your questions directly and then give a general indirect answer to the overall problem.
probably a performance gain?
You are correct that there is a Flow materialized for each IncomingConnection but there is still a performance gain to be had if a Connection has multiple requests coming from it.
What happens when request are being filtered?
In general streams do not have a 1:1 mapping between Source elements and Sink Elements. There can be 1:0, as in your example, or there can be 1:many if a single request somehow spawned multiple responses.
QueueSize vs max-open-request?
This ratio would depend on the speed with which elements are being offered to the queue and the speed with which http requests are being processed into responses. There is no pre-defined ideal solution.
GENERAL REDESIGN
In most cases a Source.queue is used because some upstream function is creating input elements dynamically and then offering them to the queue, e.g.
val queue = ??? //as in the example in your question
queue.offer(httpRequest1)
queue.offer(httpRequest2)
queue.offer(httpRequest3)
This is poor design because whatever entity or function that is being used to create each input element could itself be part of the stream Source, e.g.
val allRequests = Iterable(httpRequest1, httpRequest2, httpRequest3)
//no queue necessary
val allResponses : Future[Seq[HttpResponse]] =
Source(allRequests)
.via(poolClientFlow)
.to(Sink.seq[HttpResponse])
.run()
Now there is no need to worry about the queue, max queue size, etc. Everything is bundled into a nice compact stream.
Even if the source of requests is dynamic, you can still usually use a Source. Say we are getting the request paths from the console stdin, this can still be a complete stream:
import scala.io.{Source => ioSource}
val consoleLines : () => Iterator[String] =
() => ioSource.stdin.getLines()
Source
.fromIterator(consoleLines)
.map(consoleLine => HttpRequest(GET, uri = Uri(consoleLine)))
.via(poolClientFlow)
.to(Sink.foreach[HttpResponse](println))
.run()
Now, even if each line is typed into the console at random intervals the stream can still behave reactively without a Queue.
The only instance I've every seen a queue, or Source.ActorRef, as being absolutely necessary is when you have to create a callback function that gets passed into a third party API. This callback function will have to offer the incoming elements to the queue.

Why do we need both Future and Promise?

As I know, Future is read-only and Promise is write-once data structure.
We need a Promise to complete a Future
For example,
object Lie extends Throwable
val lie = Future { throw Lie }
val guess = Promise[String]()
lie.onComplete { case Success(s) => guess.success("I knew it was true!")
case Failure(t) => guess.failure("I knew it was lie")}
// return type: Unit
guess.future.map(println)
// res12: scala.concurrent.Future[Unit] = List()
// I knew it was lie!
// Requires Promise to chain Future with exception
But, I can't understand why we need to have both Future and Promise
I guess Promise is required because of Future.onComplete signature
Since Future.onComplete return type is Unit,Future with possible exceptions cannot be chained
I assume Promise was introduced to overcome this limitation
But why not just change the signature of Future.onComplete ?
Changing the return type of Future.onComplete as Future[T] will enable chaining on Future with exception
and then, Future does not need Promise
For example, code above can be changed into
val lie = Future { throw Lie }
lie.onComplete {
case Success(s) => "I knew it was true!"
case Failure(t) => "I knew it was lie!"
}.map(println)
//onComplete return type is Future[String]
My question is
1) am I right? does Future not need Promise , If onComplete signature is changed from Unit to Future[T]?
2) Why Future and Promise are divided in the first place ?
UDPATE
Thanks to the repliers, Now I understand the purpose of Promise. It wasn't actually for Future chaining
If I may, can I ask you
Why onComplete returns Unit ??
It can actually return Future[T] to enable chaining Future easily
For example
Future { throw Error }.onComplete {
case Success(s) => "Success"
case Failure(t) => throw Error
}.onComplete {
case Success(s) => "Success"
case Failure(t) => throw Error
}. ...
Future.apply[T](block: => T): Future[T] is syntactic sugar for Future.unit.map(_ => block)[1]
A Future represents a value which may or may not be currently available.
A Promise represents the obligation to provide such a value at some point.
Having separate entities for Future (for reads) and Promise (for writes) means that it is easy to reason about capabilities:
When a Future is a parameter, it is a request to have some value at some point and when it is used as a return type, it's a response which may not be currently available.
When a Promise is a parameter, it is the "consumption" of responsibility of producing some value at some point, and when it is used as a return type it is the "production" of responsibility to produce the value at some point.
All in all, being able to reason about capabilities, especially in asynchronous, or even concurrent programs, is extremely valuable.
Most of the time Promises need not be used, since that is transparently handled by the Future-combinators—but when integrating with third party software or networking libraries it can be extremely useful.
For more information about interesting new features in Scala 2.12, have a look here.
1: Where Future.unit
is defined as:
val unit: Future[Unit] = Future.successful(())
am I right? does Future not need Promise , If onComplete signature is
changed from Unit to Future[T]?
You're mixing things up a little. Let's clarify.
A Future[T] represents a computation which will complete in the future. That is, you pass Future.apply a function which will execute on a thread assigned by some ExecutionContext you define.
Now, on the other hand, a Promise[T] is a way to create a Future[T], without actually creating a Future[T]. A good example for this would be the Future.successful method (which will internally consume Promise.successful):
def successful[T](result: T): Future[T] = Promise.successful(result).future
This requires no ExecutionContext and no queuing if any additional resources. It's merely a convenience wrapper that allows you to "artificially" create a Future[T].
No.
Futurecannot cover the use cases of Promise. Promise has its own value. You cannot replace Future with Promise.
Future represents a computation which is available at a later point of time. You can get the result using onComplete once the future execution is complete and you can compose futures using map, flatMap, recover and recoverWith.
Promise is one time write container, clients can subscribe to the container. When subscribed client gets a future to wait until value is written into that container called promise.
Future is read-only
You cannot use future as one time write container. You can only read from future.
But Promise is different.
What if you want to give user something, which you do not have right now but you think you will have it soon ?
That means you are promising user something which you do not have right now.
So you want to keep the user waiting till you have the thing which you will give it to user. Thats when you do p.future and generate a future so that user can wait for the result using the future.
Once you have the result which you promised to give the user, You give to user by making the future a success or failure when something bad happens (i.e by doing p.complete).
Even if onComplete return type is changed to Future[T]. Future cannot act like or serve purpose of Promise.
Creating future using Future companion object
Future can be created by using Future.apply also. In this case future created would be complete once the computation inside the future finishes.
Future is used to subscribe to the result of the time-consuming computation, whereas Promise can be a publish and subscribe model.

Is it correct to use `Future` to run some loop task which is never finished?

In our project, we need to do a task which listens to a queue and process the coming messages in a loop, which is never finished. The code is looking like:
def processQueue = {
while(true) {
val message = queue.next();
processMessage(message) match {
case Success(_) => ...
case _ => ...
}
}
}
So we want to run it in a separate thread.
I can imagine two ways to do it, one is to use Thread as what we do in Java:
new Thread(new Runnable() { processQueue() }).start();
Another way is use Future (as we did now):
Future { processQueue }
I just wonder if is it correct to use Future in this case, since as I know(which might be wrong), Future is mean to be running some task which will finish or return a result in some time of the future. But our task is never finished.
I also wonder what's the best solution for this in scala.
A Future is supposed to a value that will eventually exist, so I don't think it makes much sense to create one that will never be fulfilled. They're also immutable, so passing information to them is a no-no. And using some externally referenced queue within the Future sounds like dark road to go down.
What you're describing is basically an Akka Actor, which has it's own FIFO queue, with a receive method to process messages. It would look something like this:
import akka.actor._
class Processor extends Actor {
def receive = {
case msg: String => processMessage(msg) match {
case Success(x) => ...
case _ => ...
}
case otherMsg # Message(_, _) => {
// process this other type of message..
}
}
}
Your application could create a single instance of this Processor actor with an ActorSystem (or some other elaborate group of these actors):
val akkaSystem = ActorSystem("myActorSystem")
val processor: ActorRef = akkaSystem.actorOf(Props[Processor], "Processor")
And send it messages:
processor ! "Do some work!"
In short, it's a better idea to use a concurrency framework like Akka than to create your own for processing queues on separate threads. The Future API is most definitely not the way to go.
I suggest perusing the Akka Documentation for more information.
If you are just running one thread (aside from the main thread), it won't matter. If you do this repeatedly, and you really want lots of separate threads, you should use Thread since that is what it is for. Futures are built with the assumption that they'll terminate, so you might run out of pool threads.

Deadlocks using Akka actors as a mailbox only

I want to use Akka actors as a mailbox only, i.e. I want to create n threads, which each create 1 remote actor.
Each thread obtains a reference to all the remote actors of the other threads, such that they can send messages to eachother via their respective actors.
The actor is defined as follows:
case class Receive
case class GroupReceive(id: Symbol)
case class GroupMsg[T](id: Symbol, msg: T)
class FooParActor(val distributor: Distributor) extends Actor
with Stash {
import context._
val globalRank: Int = distributor.globalRank
def doReceive(realSender: ActorRef, ID: Symbol) {
unstashAll()
become({
case GroupMsg(ID, msg) =>
realSender ! msg
unbecome()
case GroupMsg(otherId, msg) =>
println(globalRank + ": stashing " + otherId)
unbecome()
case x => sys.error("bad msg: " + x)
}, discardOld = false)
}
def receive = {
case GroupReceive(id) =>
doReceive(sender, id)
case GroupMsg(id, x) =>
stash()
case x => sys.error("bad msg: " + x)
}
}
To read a message, the owner-thread sends GroupReceive('someSymbol) to his local actor, which in turn forwards a GroupMsg to the thread. The code from the thread's point of view to read a message looks like this:
def groupRcv[T](id:Symbol) = Await.result(aref ? GroupReceive(id), timeout.duration).asInstanceOf[T]
where aref is a reference to the local actor of this thread.
I sometimes experience deadlocks(timeouts of 5 seconds) with the above pattern, even with extremely simple usage and small messages. I narrowed the problem down to actors stalling after receiving the GroupReceive(id) message, but before entering the first case of doReceive(...): case GroupMsg(ID, msg) =>.
I made printout-traces to check that the actors actually have messages in the stash before they go to the doReceive call, and it seems that for some reason, they just don't handle them. Can the code I presented above go to a state where a GroupMsg() gets lost from a FooParActor's stash? Or is there any other way the actor can go to a deadlock after receiving a GroupReceive() message?
You are using Await.result() but not sharing where you do that: if you call groupRcv on a thread on which your actors are supposed to be running then you can of course have starvation (i.e. the target actor does not have a thread available to run on, so it will never complete the request).
It seems that you are mixing thread-based concurrency with actors in an unhealthy fashion, but since you only hint at it and don’t show the code I can only give you the broad advice to not do that. When programming actors, forget about threads; those are managed by Akka. In particular do not misuse Akka’s threads (i.e. the Await.result would probably work on your own external thread pool, although there is nearly always a better alternative).
In the end, if you are using actors just to make “a thread with a mailbox” then Akka cannot help you and you will run into all the usual traditional concurrency pitfalls.