On a recent project, I had need again to use GeoIP (by the lovely people at MaxMind). I decided to take some time to benchmark a handful of various GeoIP gems, both for GeoIP v1 (legacy) and the newer GeoIP v2.
I decided to benchmark the extraction of 3 pieces of data, using the GeoIP Lite City database:
This is part 5 of my ongoing series on Ruby on Rails performance. Today’s topic is a bit more complicated that some of the previous parts.
I’m going to explore an alternative to using the method 1ActiveRecord::Base.to_xml.
Before I get into that, I probably should explain why. After all, 1ActiveRecord::Base.to_xml is really easy to use. The alternative I’m going to demonstrate isn’t very easy to use.
The problem is that 1ActiveRecord::Base.to_xml can be really slow—even after making the optimizations I’ve explored in the previous parts to this series. For fairly simple AR objects, this won’t be a problem. However, when you have a deep object hierarchy and need substantial portions of it to be included in the XML response, it becomes a problem.
One instance where I encountered this was a nested has_many tree. That is, the object tree looks something like this:
ParentObject has_many ChildObjects
Each ChildObject has_many OtherObjects
The XML output is often 100k or more, and representing a few dozen or more total objects.
In part 3 of my Ruby on Rails Performance Series we’ll look at how to improve the parsing speed of XML content.
Rails will automatically parse XML in two key places:
1. When XML content is sent to a controller/action pair via POST or PUT. (Your app is an API server.)
2. When XML content is received from an ActiveResource request. (Your app is an API client.)
The default XML parser is REXML. It’s written in pure Ruby which makes it very portable. And slow. Very slow.
So, we need to replace REXML with something faster. The key is the 1libxml-ruby gem which is just a binding to the very fast libxml2 C library.
libxml-ruby has a different set of methods from REXML and is not drop in compatible. If you’re planning on moving your own REXML parsing code to libxml-ruby, it will take some work. However, it’s worth it. In one particular project, I had about a 100x speed improvement!
Rails, as it often does, makes the switch to libxml-ruby super easy. First, install the gem (requires a functioning compiler, the libxml2 library, and headers for libxml2):
1gem install libxml-ruby
Then, just add a new file to 1config/initializers/. Let’s call it 1config/initializers/xml_config.rb. In that file, put this:
1ActiveSupport::XmlMini.backend = 'LibXML'
This was only added in recent versions of Rails, I believe in 2.3.
Lately I’ve been doing a lot of API type work in Rails — both client and server. It mostly uses Rails’ default XML generation and consumption.
Along the way I’ve discovered that the default performance of XML and related things on Ruby and Rails is, shall we say, less than amazing. On the bright side, there are a number of ways to improve matters.
So, I’m going to do a series on improving the performance of Rails’ API-related stack.
I’ll start off with serialized columns in ActiveRecord.