Obtaining better data from failed futures

380 views
Skip to first unread message

Luciano Leggieri

unread,
Jul 12, 2013, 4:16:41 PM7/12/13
to scala...@googlegroups.com
In the following code:

object Xxx extends App {

  def makeFuture: Future[String] = Future {
    throw new Exception("An exception")
  }

  def main() {
    val anotherFuture = makeFuture map { s => s + " extended string " }

    anotherFuture.onComplete {
      case Success(s) => println(s)
      case Failure(t) => t.printStackTrace()
    }

    Thread.sleep(1000)
  }

  main()

}

I obtain the meaningless stacktrace:

java.lang.Exception: An exception
at Xxx$$anonfun$makeFuture$1.apply(Xxx.scala:10)
at Xxx$$anonfun$makeFuture$1.apply(Xxx.scala:10)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at scala.concurrent.impl.ExecutionContextImpl$$anon$3.exec(ExecutionContextImpl.scala:107)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

Something similar happens in this case:

object Xxx extends App {

  def makeFuture: Future[String] = Future {
    "Some string"
  }

  def main() {
    val anotherFuture = makeFuture map { s => throw new Exception("An exception") }

    anotherFuture.onComplete {
      case Success(s) => println(s)
      case Failure(t) => t.printStackTrace()
    }

    Thread.sleep(1000)
  }

  main()

}

getting this stacktrace:

java.lang.Exception: An exception
at Xxx$$anonfun$1.apply(Xxx.scala:14)
at Xxx$$anonfun$1.apply(Xxx.scala:14)
at scala.concurrent.Future$$anonfun$map$1.apply(Future.scala:253)
at scala.concurrent.Future$$anonfun$map$1.apply(Future.scala:249)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:29)
at scala.concurrent.impl.ExecutionContextImpl$$anon$3.exec(ExecutionContextImpl.scala:107)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

I'd like to obtain more data from these exceptions. For instance, in the first case, I can tell it was a Future made in the "makeFuture" method, but I'd like to know that I called this method from my "main" method.
In the latter case all I know is that it's a future map in my Xxx class.

Of course this are simple cases, but I'm dealing with tenths of Future calls that are composed together (using fallbackTo, Future.sequence, etc). So it's hard for me to reason about what happened when I have to deal with future failures.

I know how future works, execution contexts and runnable blocks, but that doesn't make my issue easier. My ideal would be to have a stack trace snapshot when the Promise instance is created, and attach it to the exception failure. In the meantime, are there any best practices that I can use to deal with these situations?

(For the record, most of my future failures come from Spray client, or Akka ask timeouts, but it would be the same for any kind of future failure).

Thanks!

Som Snytt

unread,
Jul 12, 2013, 5:17:42 PM7/12/13
to Luciano Leggieri, scala-user

Even from my limited experience, I can agree that tracing complex interactions among futures is difficult, especially across multiple executors.

I understand that is one reason people like actors.

It would interesting to see if it's possible for a future payload to carry trace info conveniently.

For example, flatMap would also have to smuggle the trace forward into the new future.

I wonder if a tracing ExecutionContext would be sufficient.  (It could figure out who is submitting a job and could know when a job fails.)

For the record, though, a truly meaningless stack trace is like this old one from six months ago:


java.lang.IllegalStateException: Promise already completed.
        at scala.concurrent.Promise$class.complete(Promise.scala:55)
        at scala.concurrent.impl.Promise$DefaultPromise.complete(Promise.scala:58)
        at scala.concurrent.Promise$class.failure(Promise.scala:107)
        at scala.concurrent.impl.Promise$DefaultPromise.failure(Promise.scala:58)
        at scala.concurrent.Future$$anonfun$map$1.apply(Future.scala:257)

        at scala.concurrent.Future$$anonfun$map$1.apply(Future.scala:249)
        at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:29)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
        at java.lang.Thread.run(Thread.java:722)

At least you've got a line number.



--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Nils Kilden-Pedersen

unread,
Jul 12, 2013, 6:22:52 PM7/12/13
to Som Snytt, Luciano Leggieri, scala-user
On Fri, Jul 12, 2013 at 4:17 PM, Som Snytt <som....@gmail.com> wrote:

Even from my limited experience, I can agree that tracing complex interactions among futures is difficult, especially across multiple executors.

I understand that is one reason people like actors.

It would interesting to see if it's possible for a future payload to carry trace info conveniently.

For example, flatMap would also have to smuggle the trace forward into the new future.

I wonder if a tracing ExecutionContext would be sufficient.  (It could figure out who is submitting a job and could know when a job fails.)

MACROS!!

Johannes Rudolph

unread,
Jul 16, 2013, 6:32:28 AM7/16/13
to Nils Kilden-Pedersen, Som Snytt, Luciano Leggieri, scala-user
Here's a proof-of-concept code how to use source location info (gathered through a macro) to annotate exceptions with more information about the combination path:

Johannes

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

Luciano Leggieri

unread,
Jul 16, 2013, 9:07:33 AM7/16/13
to scala...@googlegroups.com
Thanks! I'll take a look at that. I was already desisting and trying to rewrite the code using actors :(

Nils Kilden-Pedersen

unread,
Jul 16, 2013, 3:51:45 PM7/16/13
to Johannes Rudolph, Som Snytt, Luciano Leggieri, scala-user
Can we get this into 2.11?

:-)

Naftoli Gugenheim

unread,
Jul 17, 2013, 7:01:06 AM7/17/13
to scala-user
+1
Reply all
Reply to author
Forward
0 new messages