On March 8th, PostRank Labs released Goliath, a new Ruby-based web framework for asynchronous processing of web requests. Based on Ruby 1.9 Fibers and EventMachine, Goliath has been in production with PostRank since early 2010, reliably processing a sustained rate of 500 requests per second. With Goliath’s unveiling, continual improvements in Ruby and JVM-based runtime environments and proposed performance improvements for Rails 3.1, scaling Ruby web applications is more attainable now than ever before.
Having tested Goliath on MRI Ruby, Rubinius and JRuby, PostRank Labs currently recommends using running with MRI Ruby, where rountrip performance has been clocked at ~0.33ms, or about 3,000 requests per second.
Running Goliath on JRuby was quite a bit slower. However, use of Ruby 1.9 Fibers in JRuby by Lukas Stadler shows that JRuby may eventually run faster:
“I was hoping to see some improvements, and that maybe JRuby will even surpass the native Ruby implementation in fiber performance. … I somewhat expected JRuby with coroutine support to win over JRuby without by a large margin (because Threads are used to emulate fibers), but being faster than Ruby is of course a nice bonus!”
Based Rack and EventMachine, Goliath’s Ilya Grigorik does, in fact, attribute it’s ease-of-use to its use of Ruby 1.9.2 Fibers:
“The primary reason for Ruby 1.9 in Goliath is due to the introduction of Fibers. What are Ruby Fibers? They are continuations which allow us to arbitrarily suspend and resume any processing function (aka, cooperative scheduling). What this gave us is the ability to transparently pause and resume any IO operation at will, without even exposing this interaction to the developer. Hence, by leveraging fibers, we could hide all the callback code under the hood, expose a "synchronous looking API" within Goliath, and at the same time preserve all the nice properties of running within an event-loop.Net result? We could get rid of all the callbacks in our Goliath API's, which drastically simplified our code, and made it readable and much easier to maintain. In fact, new developers starting with Goliath at PostRank could be completely oblivious to the fact that they were writing an async app - at that point, we knew we hit on a model that is worth pursuing.”
With the maturity of multi-core processors, pressure to scale increasingly lucrative 'social' web applications and emerging concurrency options like Goliath, the Ruby community continues to push innovation in the state of concurrency programming.
Applications like Goliath and improvements to Rails 3.1, such as Automatic Flushing, promise to improve Ruby application performance even further. Management of deployed Rails application processes also continue to improve with easier-to-use and increasingly reliable cloud host providers like Rackspace (Managed Hosting), Salesforce (Heroku), and Engine Yard.
But, as reliable as cloud hosts may be, many still don’t feel they can afford to deploy-and-forget their entire online business - at least not yet. Hands-on approaches to scaling Ruby applications are still a critical need, if not for cloud providers themselves, for the individual app builders. To meet their needs, both shared-memory and shared-nothing approaches to running code parallel must be considered.
And an increasingly popular shared-memory approach to parallelism is the non-blocking IO event loop, reactor-based concurrency employed by EventMachine and Node.js. This is the approach taken by Goliath authors. When asked if Node.js affected Goliath in any way, this was Ilya’s response:
“… as I mentioned earlier, our own internal version of Goliath has gone through three major revisions.. And what we discovered in the process of one of those revisions was that writing asynchronous code (Ruby, or Javascript) through callbacks resulted in a codebase that became very hard to maintain, read, and test. Hence, while we watched the rise of popularity of node.js, internally we started going down a different path: we were looking for a way to untangle our code. The "callback spaghetti" and the elegance of Ruby felt at odds, and to be honest, while we love Javascript at PostRank, we didn't feel that we needed to abandon Ruby.”
So what does the future of Ruby concurrency look like? Running Goliath-based applications in a deployment environment that forks new instances as needed gives us a solid look. With this, application builders get the best of both in-process and extra-process concurrency. When asked about what he’d like to see in Goliath’s future, Ilya responded:
“Goliath is able to run on MRI Ruby, Rubinius and JRuby today. At the moment, MRI is the best performer, but there is some very promising work in JRuby that could make it an overall winner. I'm hoping we can really push the state of art in that department, since that would open a number of interesting opportunities. For example, once JRuby and Rubinius are viable production platforms, then we are running on "non GIL'ed" environments, which means that we can look into making Goliath take advantage of multiple cores within the same VM.And of course, as I mentioned earlier, there is a ton of room for improvement in the DSL and supporting frameworks - definitely something we're looking to improve moving forward.
Here are a bit more than I normally ask for but the project is very interesting and I would like readers to get to know the project and what it can do.”