=================================
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:
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
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.