I implemented a simple job processor that processes subjobs within futures (scala.actors.Futures). These futures themselves can create more futures for processing subjobs. Now, if one of these subjobs throws an exception, i want the job processor to reply with an error message for that job. I have a workaround solution for discovering failed subjobs, but i'm not sure if that's the best solution. Basically it works like this:
sealed trait JobResult
case class SuccessResult(content: String) extends JobResult
case class FailedResult(message: String) extends JobResult
for(subjob <- subjobs) yield {
future {
try {
SuccessResult(process(subjob))
} catch {
case e:Exception => FailedResult(e.getMessage)
}
}
}
The result at the top level is a recursive List of Lists of Lists... of JobResults. I recursively search the List for a failed Result and then return an error or the combined result depending on the types of results.
That works but i'm wondering if there's is a more elegant/easier solution for dealing with exceptions in futures?
The way you do it now, is essentially what scala.Either was designed for. See http://www.scala-lang.org/api/current/scala/Either.html
Modern scala futures are like Either in that they contain either a successful result or a Throwable. If you re-visit this code in scala 2.10, i think you'll find the situation quite pleasant.
Specifically, scala.concurrent.Future[T] technically only "is-a" Awaitable[T], but _.onComplete and Await.ready(_, timeout).value.get both present its result as a scala.util.Try[T], which is a lot like Either[Throwable, T] in that it's either the result or an exception.
Oddly, _.transform takes two mapping functions, one for T => U and one for Throwable => Throwable and (unless i'm missing something) there's no transformer that maps the future as Try[T] => Try[U]. Future's .map will allow you to turn a success into a failure by simply throwing an exception in the mapping function, but it only uses that for successes of the original Future. Its .recover, similarly can turn a failure into a success. If you wanted to be able to change successes to failures and vice-versa, you'd need to build something yourself that was a combination of _.map and _.recover or else use _.onComplete to chain to a new scala.concurrent.Promise[U] like so:
import scala.util.{Try, Success, Failure}
import scala.concurrent.{Future, Promise}
import scala.concurrent.ExecutionContext
def flexibleTransform[T,U](fut: Future[T])(f: Try[T] => Try[U])(implicit ec: ExecutionContext): Future[U] = {
val p = Promise[U]
fut.onComplete { res =>
val transformed = f(res)
p.complete(transformed)
}
p.future
}
which would be used like so:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Await
import scala.concurrent.duration.Duration.Inf
def doIt() {
val a: Future[Integer] = Future {
val r = scala.util.Random.nextInt
if (r % 2 == 0) {
throw new Exception("we don't like even numbers")
} else if (r % 3 == 0) {
throw new Exception("we don't like multiples of three")
} else {
r
}
}
val b: Future[String] = flexibleTransform(a) {
case Success(i) =>
if (i < 0) {
// turn negative successes into failures
Failure(new Exception("we don't like negative numbers"))
} else {
Success(i.toString)
}
case Failure(ex) =>
if (ex.getMessage.contains("three")) {
// nevermind about multiples of three being a problem; just make them all a word.
Success("three")
} else {
Failure(ex)
}
}
val msg = try {
"success: " + Await.result(b, Inf)
} catch {
case t: Throwable =>
"failure: " + t
}
println(msg)
}
for { _ <- 1 to 10 } doIt()
which would give something like this:
failure: java.lang.Exception: we don't like even numbers
failure: java.lang.Exception: we don't like negative numbers
failure: java.lang.Exception: we don't like negative numbers
success: three
success: 1756800103
failure: java.lang.Exception: we don't like even numbers
success: 1869926843
success: three
failure: java.lang.Exception: we don't like even numbers
success: three
(or you could "pimp" Future into a RichFutureWithFlexibleTransform with an implicit def and make flexibleTransform a member function of that, dropping the fut param and simply using this)
(even better would be to take Try[T] => Future[U] and call it flexibleFlatMap so you could do async things in the transform)
Related
I am using monix tasks and i am trying to catch a Throwable and then convert to a custom error. I have removed/changed the code to be simple and relevant. This is the code (question follows after the code snippet):
import io.netty.handler.codec.http.HttpRequest
import monix.reactive.Observable
import io.netty.buffer.ByteBuf
import monix.eval.Task
import com.mypackage.Response
private[this] def handler(
request: HttpRequest,
body: Observable[ByteBuf]
): Task[Response] = {
val localPackage = for {
failfast <- Task.eval(1 / 0)
} yield failfast
// Failure case.
localPackage.onErrorRecoverWith {
case ex: ArithmeticException =>
print(s"LOG HERE^^^^^^^^^^^^^^^")
return Task.now(
Response(HttpResponseStatus.BAD_REQUEST,
None,
None)
)
}.runAsync
// Success case.
localPackage.map { x =>
x match {
case Right(cool) =>
Response(
HttpResponseStatus.OK,
None,
cool
)
case Left(doesntmatter) => ???
}
}
}
I am able to see the print statement but the expected Task.now(Response(... is not being returned. Instead the method that calls the handler method is throwing an error. How do i make it return the Task[Response] ?
The success case works, the failure case does not.
Edit #1 : Fix errors in scala code.
Edit #2 This is how I fixed it.
// Success case.
localPackage.map { x =>
x match {
case Right(cool) =>
Response(
HttpResponseStatus.OK,
None,
cool
)
case Left(doesntmatter) => ???
}
}.onErrorRecoverWith {
case ex: ArithmeticException =>
print(s"LOG HERE^^^^^^^^^^^^^^^")
return Task.now(
Response(HttpResponseStatus.BAD_REQUEST,
None,
None)
)
}
I was thinking in terms of future and forgot the lazy eval nature of task. Also I understood how the CancellableFuture value was being discarded in the failure task.
Several problems with your sample.
For one this code isn't valid Scala:
val localPackage = for {
failfast <- 1 / 0
} yield failfast
I guess you meant Task.eval(1 / 0).
Also onErrorHandle does not have a Task as a return type, you were probably thinking of onErrorHandleWith. And it's a pretty bad idea to give it a partial function (i.e. a function that can throw exceptions due to matching errors) — if you want to match on that error, then better alternatives are onErrorRecover and onErrorRecoverWith, which take partial functions as arguments.
So here's a sample:
import monix.eval._
import monix.execution.Scheduler.Implicits.global
val task = Task.eval(1 / 0).onErrorRecoverWith {
case _: ArithmeticException => Task.now(Int.MinValue)
}
task.runAsync.foreach(println)
//=> -2147483648
Hope this helps.
Is it possible to asynchronously catch all errors given a list of futures?
I was thinking of something like:
Future { throw Exception } zip Future { throw Exception } recover ...
But I only receive a Throwable in the recover part. Any idea?
zip's docs clearly say it prefers errors from this over those from that (my emphasis):
def zip[U](that: Future[U]): Future[(T, U)] Zips the values of this
and that future, and creates a new future holding the tuple of their
results.
If this future fails, the resulting future is failed with the
throwable stored in this. Otherwise, if that future fails, the
resulting future is failed with the throwable stored in that.
You could implement your own function to combine two futures into Future[(Try[T], Try[U])], like:
def zipToTry[T,U](a: Future[T], b: Future[U])
(implicit executor: ExecutionContext)
: Future[(Try[T], Try[U])] = {
val t: Try[T] = a.map { Success(_) }.recover { Failure(_) }
val u: Try[U] = b.map { Success(_) }.recover { Failure(_) }
t.zip(u)
}
(i haven't tested this, but you get the idea)
I am writing a wrapper for an API and I want to do error handling for applications problems. Each request returns a Future so in order to do this I see 2 options: using a Future[Either] or using exceptions to fail the future immediately.
Here is a snippet with both situations, response is a future with the return of the HTTP request:
def handleRequestEither: Future[Either[String, String]] = {
response.map {
case "good_string" => Right("Success")
case _ => Left("Failed")
}
}
def handleRequest: Future[String] = {
response.map {
case "good_string" => "Success"
case _ => throw new Exception("Failed")
}
}
And here is the snippet to get the result in both cases:
handleRequestEither.onComplete {
case Success(res) =>
res match {
case Right(rightRes) => println(s"Success $res")
case Left(leftRes) => println(s"Failure $res")
}
case Failure(ex) =>
println(s"Failure $ex")
}
handleRequest.onComplete {
case Success(res) => println(s"Success $res")
case Failure(ex) => println(s"Failure $ex")
}
I don't like to use exceptions, but using Future[Either] makes it much more verbose to get the response afterwards, and if I want to map the result into another object it gets even more complicated. Is this the way to go, or are there better alternatives?
Let me paraphrase Erik Meijer and consider the following table:
Consider then this two features of a language construct: arity (does it aggregate one or many items?) and mode (synchronous when blocking read operations until ready or asynchronous when not).
All of this imply that Try constructs and blocks manage the success or failure of the block generating the result synchronously. You'll control whether your resources provides the right answer without encountering problems (those described by exceptions).
On the other hand a Future is a kind of asynchronous Try. That means that it successfully completes when no problems (exceptions) has been found then notifying its subscribers. Hence, I don't think you should have a future of Either in this case, that is your second handleRequest implementation is the right way of using futures.
Finally, if what disturbs you is throwing an exception, you could follow the approach of Promises:
def handleRequest: Future[String] = {
val p = Promise[String]
response.map {
case "good_string" => p.success("Success")
case _ => p.failure(new Exception("Failed"))
}
p.future
}
Or:
case class Reason(msg: String) extends Exception
def handleRequest: Future[String] = {
val p = Promise[String]
response.map {
case "good_string" => p.success("Success")
case _ => p.failure(Reason("Invalid response"))
}
p.future
}
I'd rather use your second approach.
You could use special type for that: EitherT from the scalaz library.
It works with scalaz enhanced version of Either : \/
It could transform combination of any monad and \/ into a single monad. So using scalaz instances for scala.concurent.Future you could achieve the desired mix. And you could go further with monad transformers if you wish. Read this beautiful blog if you're interested.
Here not prettified but working with scalaz 7.1 example for you:
import scala.concurrent.duration.Duration
import scala.concurrent.{Await, Future}
import scalaz._
import scalaz.std.scalaFuture._
import EitherT._
import scala.concurrent.ExecutionContext.Implicits.global
object EitherFuture {
type ETFS[X] = EitherT[Future, String, X]
val IntResponse = "result (\\d+)".r
def parse(response: Future[String]) =
eitherT(response map {
case IntResponse(num) ⇒ \/-(num.toInt)
case _ ⇒ -\/("bad response")
})
def divideBy2(x: Validation[String, Int]) =
x.ensure("non divisible by 2")(_ % 2 == 0).map(_ / 2)
def handleResponse(response: Future[String]) = for {
num ← parse(response).validationed(divideBy2)
} yield s"half is $num"
def main(args: Array[String]) {
Map(
'good → "result 10",
'proper → "result 11",
'bad → "bad_string"
) foreach { case (key, str) ⇒
val response = Future(str)
val handled = handleResponse(response)
val result = Await.result(handled.run, Duration.Inf)
println(s"for $key response we have $result")
}
}
}
I was hoping code like follows would wait for both futures, but it does not.
object Fiddle {
val f1 = Future {
throw new Throwable("baaa") // emulating a future that bumped into an exception
}
val f2 = Future {
Thread.sleep(3000L) // emulating a future that takes a bit longer to complete
2
}
val lf = List(f1, f2) // in the general case, this would be a dynamically sized list
val seq = Future.sequence(lf)
seq.onComplete {
_ => lf.foreach(f => println(f.isCompleted))
}
}
val a = FuturesSequence
I assumed seq.onComplete would wait for them all to complete before completing itself, but not so; it results in:
true
false
.sequence was a bit hard to follow in the source of scala.concurrent.Future, I wonder how I would implement a parallel that waits for all original futures of a (dynamically sized) sequence, or what might be the problem here.
Edit: A related question: https://worldbuilding.stackexchange.com/questions/12348/how-do-you-prove-youre-from-the-future :)
One common approach to waiting for all results (failed or not) is to "lift" failures into a new representation inside the future, so that all futures complete with some result (although they may complete with a result that represents failure). One natural way to get that is lifting to a Try.
Twitter's implementation of futures provides a liftToTry method that makes this trivial, but you can do something similar with the standard library's implementation:
import scala.util.{ Failure, Success, Try }
val lifted: List[Future[Try[Int]]] = List(f1, f2).map(
_.map(Success(_)).recover { case t => Failure(t) }
)
Now Future.sequence(lifted) will be completed when every future is completed, and will represent successes and failures using Try.
And so, a generic solution for waiting on all original futures of a sequence of futures may look as follows, assuming an execution context is of course implicitly available.
import scala.util.{ Failure, Success, Try }
private def lift[T](futures: Seq[Future[T]]) =
futures.map(_.map { Success(_) }.recover { case t => Failure(t) })
def waitAll[T](futures: Seq[Future[T]]) =
Future.sequence(lift(futures)) // having neutralized exception completions through the lifting, .sequence can now be used
waitAll(SeqOfFutures).map {
// do whatever with the completed futures
}
A Future produced by Future.sequence completes when either:
all the futures have completed successfully, or
one of the futures has failed
The second point is what's happening in your case, and it makes sense to complete as soon as one of the wrapped Future has failed, because the wrapping Future can only hold a single Throwable in the failure case. There's no point in waiting for the other futures because the result will be the same failure.
This is an example that supports the previous answer. There is an easy way to do this using just the standard Scala APIs.
In the example, I am creating 3 futures. These will complete at 5, 7, and 9 seconds respectively. The call to Await.result will block until all futures have resolved. Once all 3 futures have completed, a will be set to List(5,7,9) and execution will continue.
Additionally, if an exception is thrown in any of the futures, Await.result will immediately unblock and throw the exception. Uncomment the Exception(...) line to see this in action.
try {
val a = Await.result(Future.sequence(Seq(
Future({
blocking {
Thread.sleep(5000)
}
System.err.println("A")
5
}),
Future({
blocking {
Thread.sleep(7000)
}
System.err.println("B")
7
//throw new Exception("Ha!")
}),
Future({
blocking {
Thread.sleep(9000)
}
System.err.println("C")
9
}))),
Duration("100 sec"))
System.err.println(a)
} catch {
case e: Exception ⇒
e.printStackTrace()
}
Even though it is quite old question But this is how I got it running in recent time.
object Fiddle {
val f1 = Future {
throw new Throwable("baaa") // emulating a future that bumped into an exception
}
val f2 = Future {
Thread.sleep(3000L) // emulating a future that takes a bit longer to complete
2
}
val lf = List(f1, f2) // in the general case, this would be a dynamically sized list
val seq = Future.sequence(lf)
import scala.concurrent.duration._
Await.result(seq, Duration.Inf)
}
This won't get completed and will wait till all the future gets completed. You can change the waiting time as per your use case. I have kept it to infinite and that was required in my case.
We can enrich Seq[Future[T]] with its own onComplete method through an implicit class:
def lift[T](f: Future[T])(implicit ec: ExecutionContext): Future[Try[T]] =
f map { Success(_) } recover { case e => Failure(e) }
def lift[T](fs: Seq[Future[T]])(implicit ec: ExecutionContext): Seq[Future[Try[T]]] =
fs map { lift(_) }
implicit class RichSeqFuture[+T](val fs: Seq[Future[T]]) extends AnyVal {
def onComplete[U](f: Seq[Try[T]] => U)(implicit ec: ExecutionContext) = {
Future.sequence(lift(fs)) onComplete {
case Success(s) => f(s)
case Failure(e) => throw e // will never happen, because of the Try lifting
}
}
}
Then, in your particular MWE, you can do:
val f1 = Future {
throw new Throwable("baaa") // emulating a future that bumped into an exception
}
val f2 = Future {
Thread.sleep(3000L) // emulating a future that takes a bit longer to complete
2
}
val lf = List(f1, f2)
lf onComplete { _ map {
case Success(v) => ???
case Failure(e) => ???
}}
This solution has the advantage of allowing you to call an onComplete on a sequence of futures as you would on a single future.
Create the Future with a Try to avoid extra hoops.
implicit val ec = ExecutionContext.global
val f1 = Future {
Try {
throw new Throwable("kaboom")
}
}
val f2 = Future {
Try {
Thread.sleep(1000L)
2
}
}
Await.result(
Future.sequence(Seq(f1, f2)), Duration("2 sec")
) foreach {
case Success(res) => println(s"Success. $res")
case Failure(e) => println(s"Failure. ${e.getMessage}")
}
In my current method, I am trying to make a series of calls and if any of them fail, I want to be able to continue running the remainder (while capturing the Exception that was thrown). I am having a hard time figuring this out in Scala.
So in this example, I want to kick off each of these calls - RunA, RunB and RunC but if RunB throws an exception, I want to print that and continue kicking off RunC after that.
var result = Try {
new RunA()
new RunB()
new RunC()
} catch {
case e: Throwable => e.printStackTrace()
false
}
Outside of having them all individually wrapped in a Try/Catch, I am sure there are better ways to do this which is why I am hoping someone can help with this.
I looked at the 'Ignoring' exception but it appears to completely ignore the exception which I want to atleast log.
Thanks!
First, don't mix try { ... } catch { ... } up with scala.util.Try{ ... }.
You can
import scala.util._
val runA = Try{ new RunA }
val runB = Try{ new RunB }
val runC = Try{ new RunC }
and then deal with the exceptions as you see fit. For instance, if you want to print and continue, you could deal with the try statements right there:
def getOrPrint[A](f: => A): Option[A] = Try{ f } match {
case Success(x) => Some(x)
case Failure(e) => e.printStackTrace; None
}
getOrPrint{ new RunA }
...
There can be more elegant ways for such things with scalaz (e.g. read an article here for some inspiration: http://johnkurkowski.com/posts/accumulating-multiple-failures-in-a-ValidationNEL/), but with "only" Scala you can do something like this:
import scala.reflect.ClassTag
import scala.util.{Try, Success, Failure}
def tryAndLog[T: ClassTag] = Try {
implicitly[ClassTag[T]].runtimeClass.newInstance.asInstanceOf[T] // new instance
} match {
case Success(_) => true
case Failure(ex) => ex.printStackTrace ; false
}
def tryRunAll = {
val A = tryAndLog[RunA]
val B = tryAndLog[RunB]
val C = tryAndLog[RunC]
A && B && C // returns true if all invocations succeeded, false otherwise
}
You are mixing scala.util.Try with try {} catch {} which are different concepts. Try wraps function into Success(result) or Failure(error) class, and try-catch is like Java try-catch. I suggest you something like this:
class RunA
class RunB
class RunC
class Result(a: RunA, b: RunB, c: RunC)
implicit class LogFailure[T](t: Try[T]) {
def logFailure: Try[T] = t match {
case scala.util.Failure(err) => err.printStackTrace(); t
case _ => t
}
}
val tryA= Try(new RunA())
val tryB= Try(new RunB())
val tryC = Try(new RunC())
val result: Try[Result] = for {
a <- tryA.logFailure
b <- tryB.logFailure
c <- tryC.logFailure
} yield {
// do smth with a, b, c
new Result(a, b, c)
}
If A, B, C will be successful you'll get Success(Result) if one of them failure you'll get Failure with first exception, however all of them will be logged (printed stack trace)