Hosting Rails is a bitch

No matter what anyone says, hosting Rails applications is - and is likely to remain for the foreseeable future - a cast-iron bitch. An expensive, slow one, too.

I’m no stranger to web hosting. I set up my first web server over 10 years ago - manually, of course, none of these control panels, and have done it many times since, as technology changed and languages shifted. I’ve been responsible for the production web presence of a number of companies, and along the way I’ve dabbled with a number of server-side application technologies, including ColdFusion and even Java (ugh!) but mostly PHP, since that’s what everyone uses.

And for good reason. PHP has many flaws as a language, mainly to do with security and the hideous mess most projects in PHP comprise, but it’s a joy to deploy. Here’s how it works.

PHP is actually two things. It’s a language, and a compiled executable which will interpret and act on commands issued in that language. It integrates nicely with the most popular, and largely considered best, front-end web server software, apache, so that what happens when a PHP web page is requested is as such:

  1. Apache reads the web page, typically page.php, and determines it requires action by the PHP interpretor, and creates an instance of that
  2. The commands in the page are relayed to the PHP interpretor instance
  3. The interpreter processes the commands and performs whatever action is required, typically HTML generation and/or database actions
  4. Apache receives back the data and serves it inline with the rest of the web page.

This all happens extremely quickly, and since PHP is integrated so tightly with Apache, you only have to worry about maintaining that one application. Sure, you configure PHP - via a single .ini file. Everything else is Apache all the way, and it’s a highly reliable and very fast way to serve web pages.

An especially welcome benefit from this arrangement is that you only require one Apache server running to serve any number of PHP applications. You can easily configure Apache to serve any number of domains via its VHosts system, any or all of which may contain active PHP pages. Apache simply instantiates (and destroys) PHP instances, which respond to the relevant pages, whenever required. The PHP executable doing the interpreting neither knows nor cares where the commands it’s interpreting come from. You’ve effectively created a “pool” of servers, any of which can respond to any PHP application, and Apache can balance its load amongst them as required.

The situation with a Rails application is very different, and not in a good way.

The current “best practise” for deploying a single Rails application is as follows:

1. Apache server front end, with a vhost interpreting the URL and relaying relevant requests to a cluster of mongrel servers, addressable by (local) IP
2. User then configures and maintains a seperate cluster of mongrel servers dedicated to that single Rails application and responding on configured IPs as set in Apache
3. Apache is further configured to go through mongrel for every non-static request.

This is then repeated for every Rails application on the box.

This has several disadvantages compared to the situation with PHP, to wit:

  1. Administrator becomes responsible for setting up, maintaining, and constantly monitoring a collection of independently-running applications, in addition to the core Apache server
  2. Mongrel servers not currently being used are not destroyed, but continue running, wasting memory (every instance of mongrel currently running on my server uses in excess of 100M each)
  3. Members in a cluster must be predefined and cannot be allocated on the fly according to need. For example, If you have 1GB of memory and 3 Rails apps, you are limited to distributing about 10 instances of mongrel between them. You must decide this in advance. If one of the applications suddenly receives a lot of hits, performance will suffer while the other mongrels, for the other apps, sit unused - the opposite of the “pool” you have access to in a PHP situation

And this is before even mentioning the underlying speed of mongrel, Ruby and Rails in general, which can only be described as horrible.

So, the sequence of events when a page request for your Rails app comes in is as follows:

  1. Apache receives request, determine the request is for your rails app
  2. Apache picks a server in your mongrel cluster, and sends the request (NOT commands, as in PHP) to it
  3. Mongrel, moving with the lightning speed of a slug through treacle, goes through the request page and sends the relevant parts to the ruby interpreter, itself not much of a speed demon, which, after a lengthy time out for self-reflection and “me time” reluctantly listens to, thinks about, thinks about some more, and then finally responds to the command. Repeat for every ruby command on the page
  4. The mongrel cluster receives and compiles the completed requests and, taking its sweet time, eventually manages to respond with the complete page
  5. Apache passes on the completed page to the client.

Not many more steps there, I admit. But it’s the speed of it which is the killer. PHP commands are detected, seperated from enclosing HTML and dispatched from within Apache, a lightning-fast and time-proven, highly reliable web server written in C. But in Rails, the job of going through the page to find Ruby requests and dispatch them is performed by Mongrel - itself written in Ruby. I don’t have any figures depicting how much slower it is, but it is - a lot.

Combine that with the inflexibility of distributing resources where needed, and having to independently spawn and monitor a seperate cluster of servers for every seperate application and you’ll understand that when people say hosting Rails is a nightmare, they’re right.

Rails has a lot of things going for it. It’s a joy to write in, mostly, and the development environment is great. It’s much, much more productive than the likes of Java while being much, much nicer than PHP.

But it’s slow as hell, and deployment sucks, and don’t let anyone tell you otherwise.

Tags: , ,

5 Responses to “Hosting Rails is a bitch”

  1. Wincent Colaiuta Says:

    While the factual details are a bit off the mark in places, the overall sentiment expressed in your post is spot-on and fully justified in my opinion.

    Minor factual points:

    - Mongrel may be written in Ruby but the performance-critical parts are written in C, and algorithmically speaking this is lightning-fast C at that (because they are largely an ultra-efficient DFA, or Deterministic Finite Automata, generated by Ragel).

    - Your discussion of PHP makes it sound like it’s just a command-line executable (in the same way that Ruby is) and although this is true, almost all use of PHP in the wild is actually done via an Apache module; I am no expert on Apache’s dispatch mechanism but my primitive understand of it is that once loaded the module resides in and shares Apache’s memory space so there’s no expensive fork/exec operation for each request nor is there any complicated or slow Inter-Process Communication.

    - You make it sound like Apache parses the requested files looking for PHP code blocks and only calls out to the PHP interpreter on hitting them; my understanding is that when it sees the file extension on the request it just passes the whole darn thing on to PHP and Apache doesn’t do any parsing at all (the PHP interpreter then interpolates the PHP output with the non-PHP parts of the source).

    As for the finer details of Apache’s dispatch mechanism for Rails and PHP, can’t really comment. But by and large I agree with everything you said.

    There seems to be a lot of hope that JRuby will be a solution, but anything which involves compiled down to Java bytecode doesn’t seem much of a solution to me, given that Java has its own deployment nightmares as well.

    Ruby 2.0 looks promising, but we’re still only talking about a modest speedup, and Ruby is only one of the links in the chain. I really don’t know what the answer is.

  2. Sho Says:

    Thanks for the corrections.

    Mongrel: C, eh? Surprised to hear that. I was beginning to think it was written in PPC-native RealBasic, running under emulation of course. Regardless, the name mongrel is ironic, because it really is a fucking dog. I love how it stops responding if idle for more than a couple of hours!

    PHP & mod_php: Hm. You’re shaking my preconceptions to the core here! Here’s what I had believed:

    I thought that mod_php basically encapsulates instances of PHP, as you say, in Apache memory space. Kind of like little PHP VMs. But I did think that mod_php parsed the page first, as I thought that would be faster, and it is Apache reading the page. The PHP instance would be loaded from /usr/bin - that’s the one with MySQL linked into it, remember. The actual internals of it I have no idea about. Anyway, I’m sure you’re right and I’m wrong - I just kind of assumed that’s how it worked!

    The whole of JRuby is the most ridiculous thing I ever heard. If you’re going to be compiling it down, you may as well compile it properly and run it as object code! Why go to another intermediatory language’s bytecode, which then needs to be run in yet another VM? And what does that mean for testing? What about Ruby plugins? Which DB adapter is used? Etc, etc. I have a lot of doubts about that “solution”. And one wonders what the point of using Rails at all will be. If you want to use Java, use Java, you could certainly do a lot worse.

    And it’s Ruby 1.9 that will apparently introduce the new VM, with a java-style JIT compiler. However, I have very low expectations that will be released anytime soon. It’s been in development over 2 1/2 years now, and is supposedly due this December .. if so, shouldn’t it be in RC’s by now? But I don’t hear any reports that it’s anything like stable. I am actually kind of worried that Ruby is a bit .. ponderous in its development.

    Even so, testing I’ve seen suggests a speedup of between 30-100% on average. While welcome, it’s nowhere near enough. It needs to be 10 times faster, not 2.

    While many people will argue that Rails can be tweaked to perform better, there’s only so far you can go before you’ve “tweaked” away so much of the development niceties that you may as well have not used it in the first place.

  3. Sho Says:

    from http://headius.blogspot.com/2007/04/paving-road-to-jruby-10-performance.html

    We perform well serving up Rails requests today, but only about 50-70% of Ruby 1.8’s performance.

    Not quite there yet, but maybe something to hope for in the future.

  4. Wincent Colaiuta Says:

    I can’t find a truly explicit reference to explain the mod_php vs PHP command-line client difference, but this page comes close. Check out this and the following paragraphs:

    If you want to use embedded PHP code in your Web pages, you need to build it in one of two different ways, depending upon whether you want to load it dynamically or have it built permanently right into the Apache server. The only difference between the interpreter and dyanmic module builds is the configure command; the sequence for building it as a static module is considerably more complex.

  5. Sho Says:

    JRuby:

    http://headius.blogspot.com/2007/04/paving-road-to-jruby-10-performance.html

    We perform well serving up Rails requests today, but only about 50-70% of Ruby 1.8’s performance.

    Not quite there yet, but maybe something to hope for in the future.

Leave a Reply