Finagle uses futures[1] to encapsulate and composeconcurrent operations such as network RPCs. Futures are directlyanalogous to threads — they provide independent and overlappingthreads of control — and can be thought of as featherweightthreads. They are cheap in construction, so the economies oftraditional threads do not apply. It is no problem to maintainmillions of outstanding concurrent operations when they arerepresented by futures.
If Twitter’s real-time feed is its most powerful asset (and it is), it’s not difficult to see a future in which Instagram, Facebook, Snapchat, or even a newcomer like Peach (yes, I am citing. Future Recursion¶. Often there is a need for a future to recurse and call itself. Twitter’s Future s implement something akin to tail-call elimination which means you will not see a stack overflow with code written in this manner.
- The latest tweets from @Paleofuture.
- Great article by Nick Bilton in Vanity Fair about the future of Twitter. Vanity Fair special correspondent Nick Bilton reports on the state of Twitter, writing that the rise of its most famous.
Futures also decouple Finagle from the operating system and runtimethread schedulers. This is used in important ways; for example,Finagle uses thread biasing to reduce context switching costs.
The harmony of this analogy has one discordant caveat: don’tperform blocking operations in a Future. Futures aren’tpreemptive; they must yield control via flatMap
. Blockingoperations disrupt this, halting the progress of other asynchronousoperations, and cause your application to experience unexpectedslowness, a decrease in throughput, and potentially deadlocks. Butof course it’s possible for blocking operations to be combined safelywith Futures as we’ll see.
Blocking or synchronous work¶
When you have work that is blocking, say I/O or a librarynot written in an asynchronous style, you should use acom.twitter.util.FuturePool
. FuturePool manages a pool ofthreads that don’t do any other work, which means that blockingoperations won’t halt other asynchronous work.
In the code below, someIO
is an operation that waits forI/O and returns a string (e.g., reading from a file). WrappingsomeIO():String
in FuturePool.unboundedPool
returns aFuture[String]
, which allows us to combine this blockingoperation with other Futures in a safe way.
Scala:
Java:
Futures as containers¶
Common examples of operations that are represented by futures are:
- an RPC to a remote host
- a long computation in another thread
- reading from disk
Note that these operations are all fallible: remote hosts couldcrash, computations might throw an exception, disks could fail, etc.A Future[T]
, then, occupies exactly one of three states:
- Empty (pending)
- Succeeded (with a result of type
T
) - Failed (with a
Throwable
)
While it is possible to directly query this state, this is rarely useful.Instead, a callback may be registered to receive the results oncethey are made available:
which will be invoked only on success. Callbacks may also be registeredto account for failures:
Sequential composition¶
Registering callbacks is useful but presents a cumbersome API. Thepower of Futures lie in how they compose. Most operations can bebroken up into smaller operations which in turn constitute thecomposite operation. Futures makes it easy to create such compositeoperations.
Consider the simple example of fetching a representative thumbnailfrom a website (ala Pinterest). This typically involves:
- Fetching the homepage
- Parsing that page to find the first image link
- Fetching the image link
This is an example of sequential composition: in order to do thenext step, we must have successfully completed the previous one. WithFutures, this is called flatMap
[3]. The result of flatMap
is a Futurerepresenting the result of this composite operation. Given some helpermethods — fetchUrl
fetches the given URL, findImageUrls
parses an HTMLpage to find image links — we can implement our Pinterest-style thumbnailextract like this:
f
represents the composite operation. It is the result of firstretrieving the web page, and then the first image link. If either ofthe smaller operations fail (the first or second fetchUrl
or iffindImageUrls
doesn’t successfully find any images), the compositeoperation also fails.
The astute reader may have noticed something peculiar: this istypically the job of the semicolon! That is not far from the truth:semicolons sequence two statements, and with traditional I/Ooperations, have the same effect as flatMap
does above (theexception mechanism takes the role of a failed future). Futuresare much more versatile, however, as we’ll see.
Concurrent composition¶
It is also possible to compose Futures concurrently. We can extendour above example to demonstrate: let’s fetch all the images.Concurrent composition is provided by Future.collect
:
Here we have combined both concurrent and sequential composition:first we fetch the web page, then we collect the results of fetchingall of the underlying images.
As with sequential composition, concurrent composition propagatesfailures: the future collected
will fail if any of the underlyingfutures do [2].
It is also simple to write your own combinators that operate overFutures. This is quite useful, and gives rise to a great amount ofmodularity in distributed systems as common patterns can be cleanlyabstracted.
Recovering from failure¶
Future Rapper Twitter
Composed futures fail whenever any of their constituent futuresfail. However it is often useful to recover from such failures. Therescue
combinator on Future
is the dual to flatMap
: whereasflatMap
operates over values, rescue
operates over exceptions. Theyare otherwise identical. It is often desirable to handle only a subsetof possible exceptions. To accommodate for this rescue
acceptsa PartialFunction
, mapping a Throwable
to a Future
:
The following retries a request infinitely should it fail with aTimeoutException
:
Future Twitter Juice Wrld
Other resources¶
- Effective Scala contains a section discussing futures
- As of Scala 2.10, the Scala standard library has its own futuresimplementation and API, described here. Note thatthis is largely similar to the API used in Finagle(com.twitter.util.Future), but there are still some namingdifferences.
- Akka’s documentation also has a section dedicated to futures.
- Finagle Block Party details why blocking is bad, and moreimportantly how to detect and fix it.
Footnotes
[1] | Finagle uses its own Future implementation by a variety of reasons(fewer context switches, interruptibility, support for continuation-local variables,tail-call elimination), but mostly because it’s preceded SIP-14 by over a year. |
[2] | Use Future.collectToTry to concurrently collect a sequence offutures while accumulating errors instead of failing fast. |
Future Twitter
[3] | The name flatMap may seem strange and unrelated to our presentdiscussion, but its etymology is impeccable: it derives from a deeper relationshipbetween the sort of sequential composition we do with futures, to a similar sort ofcomposition we can perform over collections. See the this page for more details. |
Comments are closed.