Constructing a dynamic webpage usually involves fetching data from several remote services. In this post, I'll tell you about ASAP, a small JRuby library we've open sourced that provides a concise DSL to describe these remote data dependencies and fetch them in parallel.
Parallel programming is hard
For the June 2011 Hackday, I worked with my mentor Greg Spurrier on a project that had two goals: first, we wanted to play around with Java's Netty library in JRuby. Secondly, we wanted to create a more streamlined way to build pages in our JRuby webapps.
To render a webpage, we typically call many remote services to fetch data. Some of the data can be fetched in parallel to reduce page load time; some of the data has dependencies on other data and can only be fetched sequentially. For example, if we had a page that displayed a list of your connections, we'd first have to make a call to the "cloud service" to fetch the ids of your connections and then, for each connection, we could make a call to the "profile service" to fetch their profile data.
The way we traditionally dealt with dependencies and parallelism was:
- Make a remote call to retrieve the list of users.
- Create a thread for each user in the returned list.
- Have each thread fire off a remote call to fetch that user's data.
- Join all the threads back together when they are done and render the page.
Even for this simple example, the need to deal with threads made things difficult and error prone. It only got harder for a real LinkedIn page, which typically included a much more complicated dependency tree.
ASAP makes it easier
To simplify our coding, Greg and I created an Embedded Domain Specific Language to describe these dependencies. The ASAP library can parse this DSL, express the dependencies as a tree structure and fetch the data in a maximally parallel way, all completely transparent to the programmer.
Using ASAP, the example above looks like this:
Clean, concise and fast, all without a need to mess with threads, synchronization, etc. The data you get back from ASAP matches the structure of the dependencies: namely, a tree with the user list at the root and each user's data as the children nodes:
Here is a slightly more complicated example showing more levels of dependencies:
This would result in a deeper tree structure:
Try it out!
The code is available at GitHub and can be installed using gem install asap. The gem includes a test server and some sample scripts that can be used to immediately play around with the functionality provided by Asap.