How to reduce the memory footprint of your rails app
Pre-flight
- Well just how much am I using?
- Shared FreeBSD
ps aux
- Accelerators
prstat -Z
- Freeze your rails into vendor. I cannot stress this enough, make your rails application as insulated from the deployed environment as possible. If you don't freeze your rails, you cannot take advantage of the removing unused frameworks below …
- gem unpack your gems into vendor/gems
- Just how much memory does my account have?
Environment.rb
- remove unused frameworks.
# Skip frameworks you're not going to use (only works if using vendor/rails) config.frameworks -= [ :action_web_service, :action_mailer ]
Cache and what it means to me
Learn the differences between, and how to use, page, action and fragment caching by purchasing this screencast from Peepcode.com for $9:
http://peepcode.com/products/page-action-and-fragment-caching
I found it really useful.
"Too many open files" errors
By default, there's a limit of 256 open file descriptors at any time. This is very easy to hit with a good sized rails app and will cause (at least a mongrel cluster) to die with a fail-safe error 500.
To fix this, you need to increase the limit on file descriptors for the running processes. You can do this by one of the following methods:
- run `ulimit -n 1024` in your SMF, before starting your app
- run
plimit -n 1024,65536 [pid of running process]
after the server is running.
- Add
Process.setrlimit(Process::RLIMIT_NOFILE, 1024, 65536)
to your environment.rb to increase the resource limits at runtime.
ActiveRecord tips and tricks
- ActiveRecord is smart, but don’t do:
- write a JOIN query
Post.find(:all).each do |p| p.subscribers << @user unless p.subscribers.include?(@user) end
I don’t know if it relevant but for me using reject with AR objects has led to enormous memory consumption (looks like the objects being reject’ed from the enumerable were not garbage collected)
- pull all records into memory just to throw some away
Post.find(:all).reject{|p| p.comments_closed? }
better to use
Post.find(:all, :conditions=>"comments_closed = 0") #or do Post.find_all_by_comments_closed(false) #or write the specialized finder method in model.
How to find those expensive calls
Don't fret over every line in your model. Better to spend the time optimizing the calls which are used 1000 times then once a week. So how do you find those calls? Analyze your *production* logs That is right, take the time to optimize the calls your *users* make the most.