Tracking down some application problems with Ruby on Rails, MySQL

I took care of a couple long standing issues this week and I wanted to quickly talk about them.

Tracking down Ruby/Rails memory leaks

Memory leaks are no fun. We sprouted a big leak about a month ago: Ruby VMs would get all bloated, often hitting 300-400 MB and sometimes consuming even more ridiculous amounts of memory.

I run about 20 instances on each virtual server and a bunch of misbehaving instances could quickly eat up all of the memory. Because restarting my mongrels (Thins, I mean) when they consume too much memory is pretty painless, I put off fixing it for a while.

This week, I tried using bleak_house to figure out what sorts of objects were responsible for my VM bloat. I found nothing.

Then I learned something really important about Ruby’s garbage collection and heap maintenance. I don’t know how I missed out on learning this earlier. I’m going to quote a bit from hacking on ruby’s garbage collector at lloydforge.org since he explains it well.

  • First important thing: “When ruby runs out of heap space, it first does a GC run to try to free something up, and then allocates a new heap. the new heap is 1.8 times larger than the last.”
  • Second important thing: “Because of the way ruby works, objects may _never_ be moved around in heaps. That means from the time they’re allocated to the time they’re freed they may not be moved to a new memory address”

You see where I’m going with this? Force ruby to build bigger and bigger heaps and you’ll probably never get that memory back. A single non-garbage object sitting on one of this heaps will keep it alive prevent you from getting that memory back. There is no heap compaction.

I read about some of the work that the Phusion guys had done on their ruby fork called “Ruby Enterprise Edition” and noticed that they had added a handy debugging tool: ObjectSpace.statistics.

I installed Ruby EE, added some ObjectSpace.statistics logging to each request and ran it on one server for a half-day. Once I had my results, it only took a few minutes to spot the problem requests and fix them. Phluid’s Ruby EE prints out the number of objects, size of objects, number of heaps, and size of heaps so you can spot when a request is causing the creation of a new (large) heap. The culprit was just a stupid coding error in a rarely-hit action that loaded way too much data via Activerecord. The heap would balloon to a gigantic size and although the objects loaded would all become garbage, something prevented the memory from being given back.

This was a heap-related problem but I think that these ObjectSpace statistics would be equally valuable for finding the reference-related leaks. You probably wouldn’t be able to eyeball the data like I did but you could add up the growth in # of objects and bytes across a large number of requests and come up with a few suspicious actions. The really nice thing is that I was able to set up this logging in production without impacting the site in any way.

Tracking down MySQL performance problems

If you are running MySQL, you should consider using one of the Percona patched versions: http://www.mysqlperformanceblog.com/2008/07/16/mysql-releases-with-percona-patches/

The Google SHOW INDEX_STATISTICS patch is great - you can use it as part of your index tuning and pruning because it will show you which indexes are being hit.

The microslow patch turned out to be an extremely valuable tool for finding problem queries. We have far too much query traffic to log all of it and MySQLs “slow query” log only works as whole second precision. In our case, we had some slow queries that were beating up the IO subsystem and they weren’t easy to spot until brought up the patched MySQL and set the threshold to 300 milliseconds (instead of 1 second).

I ended up breaking up a very large table into an InnoDB table with all of the important information and indexes and a MyISAM table containing the blobs (text columns). I changed a few lines of code on the application side to make things work with the new split table, and voila:

okay. I’ve run out of steam…

In other news - we hit a milestone of sorts by nearly making the Alexa 10,000 this month. We’re at 10,032 ;)

Comments (6)

  1. From experience I know memory leaks are a real pain, can be a serious problem and are often difficult to trace, so well done!

    Very interesting to see the Alexa 10,000 stats - particularly the percentage of users from other countries… I didn’t even know there was a Azerbaijan!

    Wednesday, September 10, 2008 at 3:56 am #
  2. Cyberchip wrote:

    Congratulations on Alexa! I’m a programmer/knitter and I love the site. Thanks.

    Saturday, September 13, 2008 at 6:56 pm #
  3. victori wrote:

    What do you use to aggregate those reports and graph them for server statistics?

    Monday, September 22, 2008 at 9:23 am #
  4. Lane wrote:

    I don’t know if you are aware but Ravelry is in the top 100 list of Ruby on Rails sites :-D

    http://rails100.pbwiki.com/

    Thursday, October 2, 2008 at 3:24 pm #
  5. Delphy wrote:

    The percona builds are indeed very nice - I’ve played around with them in development for my site (modthesims2.com), but one of the major things I’ve found to help is not actually changing mySQL at all… but using memcached. I’m not sure if Ravelry uses it, but it seriously cuts down on your database usage, which speeds things up - and the beauty is you can run a bunch of 512mb or 1gb memcache servers on your www or whatever boxes and they all appear as one big giant cache to the application servers. I’d seriously suggest checking it out if you haven’t already. :)

    Monday, October 20, 2008 at 8:32 am #
  6. Serene wrote:

    There was an interesting article today on RubyInside about performance diagnostics on Ruby applications.

    http://www.rubyinside.com/rubyrun-community-edition-diagnostics-reports-for-your-ruby-apps-1374.html

    Tuesday, December 9, 2008 at 11:30 am #

Trackback/Pingback (1)

  1. Websites tagged "myisam" on Postsaver on Sunday, July 26, 2009 at 4:30 am

    […] Types saved by abuzooz2009-07-25 - ANALYZE: MyISAM vs Innodb saved by PockyLovHer182009-07-25 - Tracking down some application problems with Ruby on Rails, MySQL saved by mileyfan0092009-07-25 - A Parting Shot saved by RoboRHODES2009-07-17 - MySQL […]