Holy Crap, My Rails Site is Slow

=================================
HOLY Crap, My Rails Site is Slow

Ever say this? Ever feel this? Ever wonder how to fix this? I’ll give
you some solid ideas on how to fix this.

Let’s start with a few things to make sure we’re all on the same page
here.

Your general site makeup will have:

A backend server component (Apache for instance)
A frontend server component (passenger, nginx etc.)
Your application (Rails)

Apache:

A lot of initial troubles really start at the apache end. If you edit
your apache2.conf file, I use apache2, for instance, and look over a few
simple things, scroll down the prefork section that contains:

StartServers 5 MinSpareServers 5 MaxSpareServers 10 MaxClients 150 MaxRequestsPerChild 0

One of the biggest issues I see that people make is with one line here
and that is MaxClients. What is MaxClients? Straight from apache docs:

The MaxClients directive sets the limit on the number of simultaneous
requests that will be served. Any connection attempts over the
MaxClients limit will normally be queued, up to a number based on the
ListenBacklog directive. Once a child process is freed at the end of a
different request, the connection will then be serviced.

I remember, when visiting IRC for #httpd one day, some fella telling
another, just increase that setting to 256 and you’ll be okay. The guy
he was telling this to was on a virtual server utilizing 256m of RAM.
Do you know what will happen here? Good guess maybe?

Well, before I answer that let’s look at how to properly calculate
MaxClients for any apache setup.

First, you go to your server and type Top and hit enter. Top allows you
to view your system resources, cpu, mem, etc. Press Shift+M to sort by
memory. Now then, you should be able to see some process IDs running
under different users. If you look at the command column, you will see
apache2 for instance, and in my case, www-data is the user. The mem
column tells me what percentage of memory is being used for a single
instance, per user connection attempt. So, on a 256m slice, this might
be around 2.4%. If you take 90% and divide that by 2.4% you get… 37.5
or let’s round down to 37.

Why 90% and not 100%? You still have to account for resources that your
server needs to have available to it. So, now looking back at the guy
who told the other fella to set this to 256, if we take 256 and multiply
it by 2.4 we get 614% of memory usage. 100% memory usage versus 614%
memory usage… I think you get the picture.

The point here is to make sure you properly configure apache. Make sure
you’ve calculated and optimized everything here. If you don’t, you will
create a lot of issues for yourself.

Other Apache notables:

Expires Headers, Gzip should be used for caching and compression of
content.

Passenger/Nginx:

There are a lot of articles about optimizing these areas. I have to
admit that my knowledge with either is still very slim but I’m working
with passenger and finding that it’s pretty easy to work with.

passenger-memory-stats
passenger-status

Both of these commands are really good to use with passenger so make use
of them.

I’m not going to go into optimizing passenger at all here because my
knowledge is just not strong enough to provide any details here. If
someone wants to add to this discussion with some optimization links or
techniques for both passenger and nginx, please do so!

Thanks.

Rails:

Caching

If you haven’t tried caching, you should look into it. Page caching,
action caching, and fragment caching. All three are very useful but the
differences in speed are noticeable. Page caching is faster than action
caching is faster than fragment caching. That’s the way it runs.
Depending on your app, you can use all three if necessary. For
instance, on pages that are very dynamic, or include authentication,
fragment caching might be necessary for specific areas. On static pages
with content that pretty much never changes, page caching is what you
would use. Look over all 3 and learn them.

If you want to get advanced, go with memcached. Memcached is really
great if you have a multi-tiered setup (multiple content servers) as you
can have all those content servers working in the same cache pool.

Reverse Proxy Caching is also great to implement. It puts less of a
load on your server and sets up a proxy layer where you can expire
content.

Database

Optimize your database. If you have fields that end with _id and they
are foreign keys, setup indexes on them. Make sure you use includes and
joins so that you aren’t sending multiple queries to your db.

Normalize your database properly but dont’ over-normalize your database
to the point your rails app no longer functions.

If you have some heavy work to do with your database, calculations and
such, you might want to look into setting up stored procedures in your
database. It can be a little difficult getting used to using them with
rails but it will make a world of difference when you get used to
understanding them.

Newrelic RPM

http://www.newrelic.com/features.html

This is a really useful utility to use for measuring and optimizing your
rails app and site. Look into it.

Firefox Plugins

Firebug and YSlow: Most people know about Firebug but Yslow is really
solid on grading your site.

YSlow grades out your site like such:

*
  F Use a Content Delivery Network (CDN)
*
  F Add Expires headers
*
  C Compress components with gzip
*
  A Put CSS at top
*
  A Put JavaScript at bottom
*
  A Avoid CSS expressions
*
  N/A Make JavaScript and CSS external
*
  A Reduce DNS lookups
*
  A Minify JavaScript and CSS
*
  A Avoid URL redirects
*
  A Remove duplicate JavaScript and CSS
*
  F Configure entity tags (ETags)
*
  A Make AJAX cacheable
*
  A Use GET for AJAX requests
*
  A Reduce the number of DOM elements
*
  A Avoid HTTP 404 (Not Found) error
*
  A Reduce cookie size
*
  A Use cookie-free domains
*
  A Avoid AlphaImageLoader filter
*
  A Do not scale images in HTML
*
  A Make favicon small and cacheable

Summary:

Keep in mind, I’m only touching on about 70% of the things you can do to
improve the slowness of your site. The idea is to become very familiar
with all of the different pieces of the puzzle and implement things
properly. Don’t be afraid or lazy. Optimize your site!

Take care.

Hi Joel,

On Fri, 2009-08-14 at 18:13 +0200, Alpha B. wrote:

Let’s start with a few things to make sure we’re all on the same page
here.

Your general site makeup will have:

A backend server component (Apache for instance)
A frontend server component (passenger, nginx etc.)
Your application (Rails)

Not sure we’re using the ‘back’ / ‘front’ words the same, but in my
lingo you’ve got these reversed. Apache lives ‘in front of’ passenger /
mongrel / etc. serving static content itself and directing requests for
dynamic content to them.

HTH,
Bill

Not sure we’re using the ‘back’ / ‘front’ words the same, but in my
lingo you’ve got these reversed. Apache lives ‘in front of’ passenger /
mongrel / etc. serving static content itself and directing requests for
dynamic content to them.

HTH,
Bill

Hi Bill, you are correct. My use of backend/frontend was not correct,
but yep you knew what I was trying to get at. :slight_smile:

Reverse Proxy lives in front of Apache lives in front of Passenger lives
in front of Rails.

Marnen Laibow-Koser wrote:

Alpha B. wrote:
[…]

Reverse Proxy lives in front of Apache lives in front of Passenger lives
in front of Rails.

Interesting essay; I’ll try to find time to comment in depth. But why
are you using a reverse proxy with Passenger? Or was that hypothetical?

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

hypothetical.

Alpha B. wrote:
[…]

Reverse Proxy lives in front of Apache lives in front of Passenger lives
in front of Rails.

Interesting essay; I’ll try to find time to comment in depth. But why
are you using a reverse proxy with Passenger? Or was that hypothetical?

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

On Aug 14, 12:13 pm, Alpha B. [email protected]
wrote:

Your general site makeup will have:

be around 2.4%. If you take 90% and divide that by 2.4% you get… 37.5
create a lot of issues for yourself.
Eh… not really how it works. First, the amount of client requests
only maps to the amount of Apache processes running when using the
prefork MPM. However, with the exception of fairly nasty
virtualization environments (like Virtuozzo), threaded MPMs give you
much more efficient resource usage. And even then, MPM selection and
configuration is a topic worthy of volumes of discussion on its own.

Also, linux is fantastic at being deceptive about process memory
usage. The %MEM column in top is fairly worthless when judging how
much memory Apache is really using… as is pretty much every
“default” tool. passenger-memory-stats is pretty good at this though!

And even then configuring memory limits is highly dependent on
your production environment’s configuration. If you only have one
machine, than probably no one component (webserver, DB, mail, etc)
should be allowed to use over half your available memory. Things do
get when when you add multiple servers to the mix though

And even then all this server configuration milarky is the last
(chronological, not emphatically) thing you as the Rails developer
should be be worrying about. The first step to any optimization is
finding where the major bottlenecks in your app are. Newrelic is
indeed a great tool for that. Once you’ve found the major general
areas to work on, you’ll need to break out the actual profiler (ruby-
prof is pretty good, unless you’re using Ruby 1.9 :frowning: ) and actually
get dirty. This is where it is vital to have thorough test coverage,
else who knows what you’ll mysteriously break when optimizing your
code. (and assuming your code is well structured and understandable to
begin with, don’t be afraid of making it ugly to speed things up at
this point… thats why you write tests and “elegant” code in the
beginning).

Gah sooo much more to talk about here though, I guess someone else
will contribute more to this thread. At this point I guess I’ll just
plug Enterprise Rails. Seemingly rushed execution aside, it is THE
absolute best Rails-geared book that currently exists in regards to
application structure and performance.

Good follow-up Pharrington.

My goal with the topic is to make people aware that oftentimes the
reason why a site is slow is not due to any one particular reason but
mainly due to the fact that the site, in its entirety, has not been
properly optimized.

I really do like newrelic RPM - it’s very useful.

One thing I definitely like is this:

http://railslab.newrelic.com/scaling-rails

This is probably one of the best put-together series of screencasts on
scaling rails and optimizing rails. It’s a must watch for everyone.

On Aug 14, 2:26 pm, Alpha B. [email protected]
wrote:


Marnen Laibow-Koser
http://www.marnen.org
[email protected]

hypothetical.


Posted viahttp://www.ruby-forum.com/.

I guess a load balancer is a specific type of reverse proxy.

well said … really helpful one

Good stuff. I have been following the scaling rails series and must
say, very very important for Rails developers.

The part about calculating the MaxClients for Apache → It would be
great if inexperienced people like me could learn from someone who
would be gracious enough to explain how it can be done. When I went
through that part in the configuration file, it was rather obvious
there was some optimization to be done there but had no idea how to
arrive at the golden numbers. Anyone who could provide a good thorough
run down on how to optimize Apache?

Thanks again for starting this thread.

On Aug 15, 2:25 am, Alpha B. [email protected]