Friday, January 18, 2008

Say it ain't so: Ruby on Rails not thread-safe, no prepared statements?

Maybe everyone already knows this. But I've been hearing about Ruby on Rails for over a year now, and I know people working on it, and I am reading all this stuff about putting Ruby on Rails on Glassfish and getting it into the enterprise. I did hear some mumblings about scaling issues with Ruby on Rails. But I didn't know it was as basic as Ruby on Rails not being thread-safe.

Hm, maybe this guy got it wrong. But no, here is another post. And another.

The author of the last post, Greg Luck, says something else that just blows my mind as a database programmer - Ruby on Rails doesn't support prepared statements. Really? So I checked for other sources. Yep, it's true.

Somebody in Greg's comments said something about how Greg obviously didn't know about ActiveRecord caching. Um, that's not really the point. The point is, when you do hit the database, why on earth would you force it to recompile and re-optimize the SQL each time?

I have worked with multi-threaded applications since 1988. Multi-threading is not new. Pre-compiled SQL showed its power ofter fifteen years ago, and every database vendor worth its salt supports it. How can it be possible that an entirely new framework for server-side database applications is not thread-safe and does not support prepared statements? It's one of those things that makes one's jaw drop.

Maybe I'm just completely missing something. Maybe I'm living in the stone age. Someone explain to me where I've gone wrong. I strive to be open-minded and anyone who knows me knows I regularly change my mind if I am convinced I have it wrong.

But as it stands, if someone were to suggest to me that we should use Ruby On Rails for a web-scale server-side application, I would sit him or her down for coffee (or a strong drink), explain the limitations above, and beg them to think about this very seriously, and at the minimum, please, run some tests before committing to this framework.


Anonymous said...

To some extent, you answered you own question when you said:

"an entirely new framework for server-side database applications is not thread-safe and does not support prepared statements?"

At least on the prepared-statements front, they may well get there eventually. Ruby doesn't have a database API standard like ODBC or JDBC, so the Rails team presumably sacrificed some optimizations in exchange for support for more databases, given finite programmer time. Once Ruby database drivers have a consistent API, and one that supports prepared statements (where available), Rails will probably support it.

The other thing you need to factor in is YAGNI: You Ain't Gonna Need It...if you stick to Rails' sweet spot.

As far as I'm concerned, Rails isn't aimed at building large Web applications. For smaller Web apps, the "pack of Mongrels" (multiple Rails processes) and non-prepared statements is more than adequate given today's hardware. Heck, last I knew, Twitter still ran Rails, and they're handling more traffic than ~99.44% of Web applications that need to be built. Not everything needs to scale to the moon and back (e.g., in-house applications for small businesses).

While I think the recent anti-Rails hype is overblown, there's little question in my mind that Rails did get over-promoted without sufficient "small apps, please" caveats.

Unknown said...

You know, I suspect you're right. The problem is that it was over-hyped, and now there is backlash based on incorrect assumptions about what Rails can do.

I know that Twitter runs Rails, and perhaps it doesn't hit these issues because it's very partitioned (only one user can write a particular twitter feed, and there are many readers). I also heard Twitter hit some nasty scaling issues at first, so I'd love to understand what they did to solve it.

Note I didn't say "don't use Rails". I said at least test it first, based on your specific requirements. We're all doing that, right? ...

Jeremy Weiskotten said...

As someone who uses Rails every day, I'm glad that it doesn't support multi-threading. You don't have to worry about writing thread-safe code, or tracking down weird, unreproducible bugs that are caused by obscure threading issues, or bottlenecks caused by over-eager synchronization. I did enough of that in "enterprise" C++ and Java apps. Concurrency can still be achieved through multiple processes, like a pack of mongrels -- it's not as resource-frugal but it's usually good enough and much less error prone.

I agree that prepared statements would be nice to have. I've seen queries that would definitely benefit from one-time compilation. However, that's not usually the bottleneck, especially in the "typical" Rails app. I hope that prepared statements make their way soon, ideally with relative transparency (ActiveRecord find conditions look suspiciously like prepared statements) that we get more or less for free. I suppose it could come in the form of a plugin, but it's surprising that no one has done it yet.

Twitter's scaling issues were architectural in nature, not the fault of Rails. Their bottleneck ultimately was the database, which they needed to mirror. A plugin was written within days to make this possible.

Anonymous said...

Grails (Groovy-based Rails-like framework) uses Hibernate for it persistence, so supports foreign keys and prepared statements.

Given its provenance (Java-like and JVM based) it likely has proper threading also.

Somebodies comment above about not wanting to bother with threading in their code is ignorant. In web frameworks that are well designed for threading, the application code has no threading code in it. Each user request is run in its own thread, and developers are actively discourage from putting threading in their code. The container/framework handles thread management.

Jeremy Weiskotten said...

@Anonymous: You're referring to me as ignorant.

I didn't mean that the application code has to implement thread management. If you are using a platform and framework that support multi-threading, you have to keep this in mind when accessing and updating shared resources, like global/static objects. In Struts, your controllers (Action classes) can't have mutable instance variables because the controller instances are shared between threads.

There's a reason that Java 5 included a big new concurrency API, and Brian Goetz wrote "Java Concurrency in Practice", IMO the most important book on the Java scene since "Effective Java". Speaking of "Effective Java", it advocates immutable objects because one of their benefits is inherent thread safety.

Thread-safety and concurrency do not come easy for lots of developers, including lots of experienced developers. Removing it as a factor removes a lot of headaches (admittedly adding some new ones).

Ned Wolpert said...

You are correct that Rails itself is not thread-safe, and active record does not do prepared statements. The reason active record does not do prepared statements is really because it does not benefit MySQL. There is no difference in MySQL if you use prepared statements from a performance perspective. This is not true for PostgreSQL or Oracle, but the main rails core do not use either of those. (I'm wanting to work on the prepared statements technique myself and make it a plugin, but I am annoyed that this problem exists as well.)

The second point about thread-safe has more to do with the issue of scaling by threads or scaling by multiple processes. From a Java standpoint, having one process with many threads is advantageous since the java processes are large. Apache 1.x was the opposite, where each connection was handle by one process, and each process was (supposed to be) small. PHP worked well in that model, and so do the old-school cgi scripts. Rails was originally built to run as a cgi script... originally fastcgi was recommended for production sites even.

Long story short, Rails got very far with what they have, but to grow more they need to better make use of the technology available. Developers tend to not like things that stagnate and I'm hoping to see more performance changes this year with rails. Either having it make use of non-blocking IO or start using threads or both.

Disclaimer... I'm actively using Rails for new application development, and deploying a different application now into production. Load testing has been very useful in finding areas that need help.

Anonymous said...

Catalyst, bitches.

Anonymous said...

Bitches? The rails community is so hostile, no wonder people are bailing out.


Anonymous said...

Try JRuby. You get JDBC drivers with prepared statements and multi-threading. Rails still isn't thread safe but JRuby runs a seperate thread for each Rails instance.

Anonymous said...

Um, the "bitches" comment was from a Perl person, not Ruby. (Catalyst is an MVC platform in Perl. Personally, I tried learning Catalyst but found learning both Ruby and Rails easier)

Anonymous said...

Seems Rails needs JRuby.

First spend your first few years bashing Java, then come asking for help....

I hope the Java community leaves Ruby/Rails out in the cold!

Roger Pack said...

rails 2.2 is now thread safe. And if you run under jruby it will run on multiple cores. google "ruby benchmark" and it shows that jruby is about as fast as 1.9 etc.
re: prepared statements. Still not available. Is sql parsing a performance hit?