Sounds like a Mongrel/Rails performance post on <a href="http://mongrel.rubyforge.org/">http://mongrel.rubyforge.org/</a> waiting to happen :)<br><br><div><span class="gmail_quote">On 5/23/06, <b class="gmail_sendername">Zed Shaw
</b> &lt;<a href="mailto:zedshaw@zedshaw.com">zedshaw@zedshaw.com</a>&gt; wrote:</span><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">On Tue, 2006-05-23 at 17:13 -0700, Kevin C wrote:
<br>&gt; I'm confused here.<br>&gt;<br>&gt; Does this not-thread-safe mean we can only run ONE mongrel for ONE app<br>&gt; on ONE server?&nbsp;&nbsp;Can we start another mongrel for the same app on the<br>&gt; same server but on a different port (and serve through proxy)?
<br>&gt;<br>&gt; I suppose we can run another mongrel for the same app on another server.<br>&gt;<br><br>Short explanation: Yes, this is normally what you have to do to get a<br>good production setup going.<br><br>Here's the long explanation that should help you understand what's going
<br>on.&nbsp;&nbsp;First, the Mongrel way:<br><br>1) Mongrel is thread safe as far as it will run any registered handlers<br>in threads and make sure that multiple threads have their own resources.<br>2) Mongrel HttpHandler objects are connected to URIs, but there's only
<br>one per each URI.&nbsp;&nbsp;This means that they run fast since there's no object<br>creation, but that they are very old school and a pain to develop for,<br>only useful for people who really need the speed.<br>3) Mongrel handlers are run in order using a processing chain.&nbsp;&nbsp;Let's
<br>say you register 4 handlers to &quot;/&quot;:<br>&nbsp;&nbsp;uri &quot;/&quot;, :handler =&gt; AuthenticationFilter.new<br>&nbsp;&nbsp;uri &quot;/&quot;, :handler =&gt; DoFastStuffHandler.new<br>&nbsp;&nbsp;uri &quot;/&quot;, :handler =&gt; StatisticsFilter.new
<br>&nbsp;&nbsp;uri &quot;/&quot;, :handler =&gt; DeflateFilter.new<br><br>What happens is that each of these is run in order with the same<br>HttpRequest and HttpResponse objects, they get their turn, and then the<br>final results are sent back to the user.&nbsp;&nbsp;(NOTE: Filter and Handler are
<br>both HttpHandler classes, just named differently for clarity).<br>4) When your handler runs, any instance variables (@foo) are reused by<br>all the threads.&nbsp;&nbsp;You have to work to guard these yourself.&nbsp;&nbsp;It's much<br>like old-school Java servlet programming.&nbsp;&nbsp;This is done for speed only,
<br>and most people won't be writing handlers unless they are uber advanced.<br><br>OK, so now how does rails fit into things?&nbsp;&nbsp;The problem with Rails is<br>that it's got so much code generation and automagic going on that it's
<br>really difficult to make it thread safe.&nbsp;&nbsp;Things would be different if<br>Ruby could ensure that it's code generation and class loading were<br>atomic, but it can't.&nbsp;&nbsp;Because of this Rails has to be locked while it's<br>
processing.<br><br>So, let's say you hit a Rails action with one thread, then this is what<br>happens:<br><br>1) RailsHandler first checks if the request could be page cached or is a<br>file.<br>&nbsp;&nbsp; a) If it is then the DirHandler takes over instead.&nbsp;&nbsp; THIS IS FAST
<br>AND NOT LOCKED.&nbsp;&nbsp;Done, next request.<br>2) RailsHandler determines that this isn't a file and isn't page cached,<br>so it must run the Rails Dispatcher to service the request.<br>3) LOCK!!!&nbsp;&nbsp;But only the call to Dispatcher, the rest of Mongrel keeps
<br>trucking.<br>4) Wait hours for everyone's IO.popen to a perl script that talks to ten<br>databases to produce a full index of the internet and then image map it<br>to generate a CAPTCHA.<br>5) While the above is happening, #1 and #2 still keep going processing
<br>clients.&nbsp;&nbsp;Mongrel is threaded so it happily queues clients up that are<br>all waiting at #3.<br>6) UNLOCK.&nbsp;&nbsp;#4 completes THE FIGHT BEGINS!&nbsp;&nbsp;Whichever client stuck at #3<br>gets the lock will then continue and you're back at #3.
<br>7) Continue until your IO.popen kills server.&nbsp;&nbsp;Incidentally, the results<br>of the Rails request are sent back to the client *after* the lock is<br>released.&nbsp;&nbsp;Rails actually spits its junk to a StringIO so that it can
<br>get done without worrying about socket crap, and then Mongrel deals with<br>the IO and threads from there.<br><br>(I'm mentioning IO.popen because it's incredibly evil, don't use it or<br>fork).<br><br>Now, what this generally means is if you have page caching turned on and
<br>most of your rails requests are pretty snappy, then you can most likely<br>handle quite a few concurrent users without any problems.&nbsp;&nbsp;As long as<br>clients get in and get out of the #3 through #6 LOCK/UNLOCK region<br>
you're set.&nbsp;&nbsp;It also means that you only pay a lock penalty for Rails to<br>run it's action and generate results to a StrinIO.&nbsp;&nbsp;Mongrel handles all<br>the socket garbage in a threaded way.<br><br>The problem comes when folks expect Rails to be an operating system.
<br>They have it spawning processes, managing tons of files, generating maps<br>and reports and latex output and putting them through pre-press<br>operations that take 10 minutes.&nbsp;&nbsp;When this happens you have 3 choices:<br>
<br>1) Only let one person use the application.<br>2) Start a bunch of Mongrels listening on various ports (on various<br>machines) and use pound, pen, balance, lighttpd, litespeed, or apache to<br>proxy requests for a &quot;fronting&quot; machine to the N backends.
<br>3) Rewrite your stuff to use BackgroundDRB or some other custom off-load<br>worker server, and use a nice AJAX &quot;Job in progress...&quot; set of actions<br>to keep things snapppy.&nbsp;&nbsp;Best part about this is you can actually shove
<br>a large portion of the status out into a Mongrel handler (now you know<br>why I covered it).<br><br>Now, why doesn't Mongrel do forking you may ask?&nbsp;&nbsp;All my tests found<br>that the fork available in Ruby just isn't reliable.&nbsp;&nbsp;It causes IO to
<br>get dropped, sockets to go away, files get closed at weird times, and<br>it's just generally bad news.&nbsp;&nbsp;Also, fork isn't really well supported on<br>win32.&nbsp;&nbsp;Nope, it's just loads easier to use mongrel_cluster to manage a
<br>ton of systems, or to setup a bunch of win32 services that listen on<br>different ports.<br><br>Well, hope that long answer helped you out.<br><br><br>--<br>Zed A. Shaw<br><a href="http://www.zedshaw.com/">http://www.zedshaw.com/
</a><br><a href="http://mongrel.rubyforge.org/">http://mongrel.rubyforge.org/</a><br><br><br>_______________________________________________<br>Mongrel-users mailing list<br><a href="mailto:Mongrel-users@rubyforge.org">Mongrel-users@rubyforge.org
</a><br><a href="http://rubyforge.org/mailman/listinfo/mongrel-users">http://rubyforge.org/mailman/listinfo/mongrel-users</a><br></blockquote></div><br><br clear="all"><br>-- <br>I was thinking of the immortal words of Socrates, when he said, &quot;I drank what?&quot;