Wednesday, December 24, 2008

Uh-oh! Can Rails (RoR) Handle the Load?


People have always been talking about Rails and its ability to handle scaling (here, here, and here). I was a skeptic of these haters. Because I had always made the assumption that good design would lead to good performance. I might be putting my foot in my mouth shortly.

We ran some preliminary stress tests on our site this past week. And the results were, well, unimpressive to say the least. In fairness, we're not caching everything at this point and haven't done everything we can to tweak performance, but we should have had a better go of it.

The main problem seems to be the sheer volume of database accesses. Rails just loves to talk to the database. In fact, almost everything is in the database, which is the major problem. MySQL has turned out to be our biggest bottleneck, which I guess you can blame on Rails, right?

A few things to note: We're running our DB and our application on the same box for the time being. We're using a server in the cloud, which is not really a dedicated server. So those play into the cards.

We're currently implementing memcache, starling, and workling as ways to help us boost our performance. But implementing caching requires a reworking of a lot of our code. So that's not cool.

All in all, I love developing on Rails. Is it enterprise ready? Jury will remain out on that one - for now. But I tend to think it is if you're a good architect. Twitter seems to have solved their problems. Which I initially thought was probably based on their design rather than RoR. So, did Twitter drop RoR? Nobody seems to know. Again, I remain in the camp that thinks scaling Rails is doable. I'm just not a pro at it yet. And we're going to have some growing pains, as we would with any technology.

4 comments:

Gene said...

Independent of which technology you choose to build your application in, sooner or later you will run in scalability issues. Rails is no different when it comes to scalability than anything else out there. Based on what I read here, you haven't done any performance optimizations yet. The fastest and easiest way to improve performance is to add caching – hitting database with every request will be slow no matter how you look at it. Before going to memcache-like solutions, I suggest looking at Rails built-in caching support first, which could be added without major changes (http://guides.rubyonrails.org/caching_with_rails.html). Second, I am guessing, you haven't optimizations your database queries and database server either – you can significantly speed up MySQL with proper configuration and indexes.
Finally, if you still think Rails might not scale, instead of looking at twitter.com look at yellowpages.com – they get about 40M unique visitors a month (~10x what twitter gets) and, you guessed right, they do run Rails.

Lefty said...

@Gene - Thanks for the comments. You are right in that we haven't implemented much in terms of optimization. While I know there is much that can be done and we will do, I was surprised at the benchmarking scores we measured. I would have expected performance to have been a bit better. We did a real basic benchmark of several of our dynamic pages vs. our static content (load).

I do share your opinion in that we'll be able to scale. The issue is more at what expense? Rails, as a language, hits the database more than anything I've seen before. Since the database layer is partially hidden (meaning Rails handles it), it's more difficult to actually tweak what is happening. With Java, my last programming language to play with, you controlled all aspects of the development, so you could easily tell where you could tweak/improve the code.

Finally, in terms of Twitter vs. Yellowpages, I'd think that unique visitors a month is irrelevant. Twitter users use the system much more, so their uniques wouldn't really factor into performance issues. There are quite a few large scale sites working on Rails. But our application is incredibly intensive in terms of the data we have to manipulate. Also - Yellowpages.com is a large company with a great many resources at its disposal. As a startup, we can't just throw money at the problem, since we don't have any.

I'll keep you updated on our experience with Rails. I'm hopeful, just being reserved with our approach.

Gene said...

Left,

Good thoughts.

One thing to remember when you look at your benchmark results is that you are running in a virtual hosting environment. Depending on how visualizations is done, the performance might be much poor than equivalent stand-alone server.

I worked on applications written Java as well as C#, and all of them suffered from the same exact issue you are facing now. Nowadays memory is cheap, so adding memory caching substantially increasing your application performance. Just like Java or .NET frameworks, rails provide ways to cache parts of your applications (see the link in my previous post).

I don't know the nature of your application, however based on your description it sounds like the bottleneck is in database reads, not writes. If that's the case, then database reads (queries) are directly reflected by how many requests your rails applications processes, which is proportional to the amount of visitors hitting your site (I agree, they don't have to be unique visitors).

When you run your benchmarking tests, aside from measuring time to process a single request, I would also measure how many requests a second your application can sustain.

Bottom line, reading from disk is the most expensive operation. To minimize it, you have to use system memory to cache disk data. You can do in the application layer (rails, .net, java, etc), or in the database layer(mysql can cash your entire db if required) or both. Without caching, your application won't scale.

Lefty said...

@Greg - thanks again for the comments. We definitely aren't getting the full picture on our virtual servers. I've looked at rails caching (your link), memcache, and MySQL caching so far - but have not yet implemented it. We'll stay on top of that and I'll report what we find on the blog.

For benchmarking we had been using requests/second, not time to load. I thought that was more telling.

Without caching, as you said, we're dead in the water. And since we're done with functionality now, we're moving on to optimizing our code. And that includes all this fun caching stuff!