I’ve been doing performance tuning on a Rails API lately and found that there are a lot of great tools and a few common performance problems that are easy to alleviate.
As long as we’ve had Rails, we’ve also had “Rails can’t scale”. And that’s sad, because it’s not true. While Rails may trade the raw performance of a compiled language, it can provide blistering fast APIs and websites. For the past fortnight I’ve been working on scaling out a high API and how has it been rewarding to watch the graph plunge to sub 100ms response times.
Before diving into performance tuning it’s important to think about what success is. There are always going to be slower parts of the app- tuning is never done. For an API, I think 100ms is a good target. I think below 200ms is acceptable. Github is able to have their API respond in 48ms, which is amazing. It’s also important to figure out which parts of the API are getting used, a 200ms but infrequently accessed action may be acceptable.
Why is it slow?
The first important task when doing performance is figuring out why it’s slow. There are really great tools for finding performance hotspots in Rails.
Rack Mini Profiler [Free, Open Source] – Rack Mini Profiler is a small badge that sits at the top of a page that’s rendered as HTML that provides a complete breakdown of how fast the page loads. It’s really easy to include if the app- 1 line if you Gemfile and you’re good to go. It only works well if your rails app also provides an HTML page that accesses the API.
Request Log Analyzer [Free, Open Source] – Request log analyzer crawls your logs and tells you what’s being accessed, how long actions are taking to respond and what’s returning errors. I like it because logs don’t lie. You can trust that the numbers are real.
Newrelic [Paid] – Newrelic is a performance tool that always watches and monitors your app performance. It does the same thing as Mini Profiler and request log analyzer but provides beautiful graphs and charts. Newrelic makes performance effortless and keeps everyone on the same page. The biggest downside I’ve found is that there isn’t always enough granularity. Sometimes Newrelic will report that a controller is taking 5 seconds but not provide any more informtaion that just “ruby”.
You don’t want to sacrifice code maintainability so I’ve found that with performing these tweaks it’s important to try them and then, if they don’t work out, take them out. It’s frustrating to add a tweak and then have to revert it but 3 months down the line you’ll be looking at the code, see the tweak and think “That must be important”. Try things, watch the graphs, get feedback from your peers, and make sure your fix is really making a difference. Performant but unmaintainable code doesn’t do anyone any good.
I’ve found a couple of common performance problems:
- Missing indexes on the database – By far the biggest problem I’ve found and the easiest to fix. This is recognizable by a simple find and slow performance. For example, if
Person.find_by_email('firstname.lastname@example.org')is slow, the persons table is probably missing an index on the email field.
- Tons of queries – If loading the API is causing tons and tons of queries, it might be a good opportunity to use
includein the query to pre-load the resource as part of another query.
- Not making use of Rails caching – Almost everything in Rails is designed with the idea that caching possible. The Rails Guide on Caching is really helpful for understanding what’s available.
- Fighting Rails caching – Rails provides really neat Key based caching. Rails will generate a new key every time a model is updated. I’ve found places in code where keys were being generated by hand by mashing different fields together with varying levels of performance and success. The cache is really helpful in ActiveModel serializers.
- Performing work during the request – Hard work like processing images or analyzing files or calculating complex math should be shuffled off to a background job system like Sidekiq.
My Project manager, Mia, observed that performance tuning is fun because you get to watch the graphs go down and know if you’re making a difference. It’s a great feeling to get APIs to a place where they’re quick. I hope that these tools and common problems help you spot places where your app could be a little faster.