Twitter is arguably the most heavily used Ruby on Rails application in the world. Almost since its inception, Twitter has fostered a wildly passionate cult following. Also from the beginning, Twitter has suffered from chronic outages under that load.
In the past month, record downtime has prompted fresh outcry within its ever-growing user base. This, along with increased attention from mainstream media has forced Twitter to publicly acknowledge these issues, and to reveal a few details about the source of these problems.
Though there has been much speculation about the source of Twitter’s performance issues, the closest we have had to a real peek under the hood has been this Twitter Developer Blog post:
Twitter is, fundamentally, a messaging system. Twitter was not architected as a messaging system, however. For expediency’s sake, Twitter was built with technologies and practices that are more appropriate to a content management system.
But wait, what exactly do they mean by “technologies and practices”? Could they mean Ruby on Rails?
Twitter and the Curse of the Framework
Ruby on Rails is a Model-View-Controller (MVC) framework. To build an application in Rails, you start by defining a collection of objects that model the things your application will do, then you write controllers that juggle your model in response to user requests, and finally you write the views that present your application to its users.
As separate as these three elements are, the performance of an app can depend largely on how they work together. If your model isn’t optimized for the way your controllers will use it, you could find yourself facing some surprising performance issues. This is the kind of problem that I expect Twitter is faced with.
Let’s look at a simplified example of what Twitter’s problem might look like on the inside.
Twitter is a site where users post short messages called Tweets, and follow the Tweets of other users. When the original developers behind the site first sat down to define their model, the simplest thing to do would have been to have a database table for all the Tweets, another table for all the users, and a third table to keep track of who is following whom.
This approach closely resembles the model you might build for a content management system (CMS) like a blog: each post has an author, so you can easily get a list of all the posts (i.e. Twitter’s public timeline), or just the posts in a given category (e.g. my tweets).
What this fails to take into account is the most common type of request that Twitter receives: show me the most recent tweets from just the users that I’m following. With a CMS-style model, this common request will generate a lot of work for the application, as it has to filter the timeline of Tweets for each individual user.
If the original developers of Twitter had realised just how many requests of this type it would be fielding, Twitter’s model would probably have been designed very differently. As mentioned in the blog post quoted above, Twitter really is a messaging system, and its model should reflect that.
Here’s how the model of a messaging system might look: whenever a user posts a Tweet, the model looks up which users are following the author and places a copy of the Tweet into those users’ private timelines. Tweets are queued up for their recipients when they’re posted, shifting the processing burden away from the more common “show me my tweets!” request.
Now, let me be clear: both models could be built with Ruby on Rails. The CMS-style model is just the direction that the framework will lead you in if you don’t stop and think about the characteristics of your application. Any good framework will handle for you the things that most web applications have in common, so you can focus on the unique aspects of your app. The curse of the framework is that it can be very tempting to let the framework take the lead in handling those unique aspects too.
The fact is, Twitter’s developers were in a hurry, and took every opportunity to let the framework do the thinking for them. Just about every general-purpose web framework out there would have led them down the same road. “The fault, dear Brutus, is not in our stars, but in ourselves.” The message here is not that Rails is a bad framework—just that no framework can do the important thinking about how an app should work. That’s what developers are for.
Like any framework, Rails has strengths and weaknesses. Those who love Rails believe that its strengths—the virtual elimination of the scutwork of building web apps using a fresh, expressive programming language—far outweigh weaknesses like scaling being a non-trivial undertaking. Truth is, if you’re building a service intended for mass adoption and you don’t design it to support that, no framework in the world is going to keep your app from collapsing in on itself.