-
Notifications
You must be signed in to change notification settings - Fork 31
Concurrent
Disclaimer: We are actively working on support Scala Futures without the need for using AspectJ. That work is in testing now. Once it is released, this module maybe deprecated.
If your application heavily uses Scala Futures, you would need to use the money-concurrent module. If you use Java Futures, sorry, but you are out of luck for the time being with this module (but, hey, if you want to contribute one!)
Add a dependency as follows for maven:
<dependency>
<groupId>com.comcast.money</groupId>
<artifactId>money-concurrent</artifactId>
<version>0.6.0_2.10</version>
</dependency>And here is a dependency for SBT:
libraryDependencies += "com.comcast.money" %% "money-concurrent" % "0.6.0"Money works by wrapping Scala Futures with an outer, wrapper future that will start and stop a trace span around the execution of your future.
It uses AspectJ in order to guarantee the propagation of the trace context (span) across all of the things that happen within your future.
You see, Scala Futures are difficult. Let's take a look at the following code snippet:
Future {
tracer.record("begin", "nested")
Some(456)
}.flatMap { case Some(v) =>
tracer.record("flatMap", "nested")
Future {v}
}.map { case _ =>
tracer.record("map", "nested")
"hello"
}What happens in the above code snippet is that 3 separate tasks are created and submitted to the current execution context. The following walks you through this case (the description is based on Scala 2.10)
- The root
Futurein the code callsFuture.applyin the standard scala library. This actually creates aPromiseand immediately wraps that Promise in aRunnableand submits it to the execution context. - The
flatmapcall will create anotherPromiseand link it to the first future via theonCompletemethod of the future. This method returns a future for the promise that is created. - The
mapcall will create yet anotherPromiseand link it to the Future returned from theflatMap.
Because these things run in the execution context in parallel with say a billion other concurrent tasks, the timing of the execution of each future that is created is non-deterministic. That is to say, by the time flatMap is actually created, the root future may already have been completed (or not).
All of these futures become more complex when you have nested futures, especially if you decide to use different execution contexts for different work (which is an entirely reasonable use case). Ensuring a common trace span across all of these futures is difficult.
tl;dr Futures in scala is not a trivial problem especially when you are trying to maintain a common trace context (trace span) across all of those futures and promises!
A note on the implementation: we are looking at being able to do this using closures and implicits. Currently, this approach felt like it intruded on the codebase more than we'd like to see for the users of the library. That said, a non-AspectJ version implementation to support Scala futures is in the works.
To trace your futures, you need to wrap your existing future declaration with a traced function.
import com.comcast.money.concurrent.Futures._
...
val future = Futures.traced("easy") {
Future {
... do something here Nostradamus ...
}
}We support more complex cases:
import com.comcast.money.concurrent.Futures._
...
val future = Futures.traced("medium") {
Future {
... do something here Nostradamus ...
}.map {
... map the value from the future to something else
}
}And even crazy-wack-funky use cases:
import com.comcast.money.concurrent.Futures._
...
traced("hard") {
Future {
Futures.traced("nested") {
Future {
"one"
}.recover {
case _ =>
"two"
}.flatMap {
case _ =>
Future{"three"}
}.map {
case _ =>
"four"
}
}
}.flatMap {
case _ =>
Future {"five"}
}.map {
case _ =>
"six"
}
}- Overview
- Configuration
- Logging Setup
- Performance Considerations
- Java Users Guide
- Scala Users Guide
- Modules
- Contributing