Fix it until you make it, a simple Ruby on Rails Performance Guide
On my experience working with several Rails Apps on production for the last couple of years I found some good rules of thumb to fix a slow endpoint on Rails and tools that can help to prevent this kind of problems even before your code is on production.
When you are running a Rails APP on production always the client, customer or finally user will tell you the same “THIS IS SO SLOW” and you will need to take a time to stop, benchmark, make the fix and stop the fire before its to late. Who like midnight call on a Friday from a grumpy customer, boss or whoever telling that your App is slow as hell or it doesn't work? You’re right! Nobody, so this is how I have found to deal with this kind of issues.
Know your Enemy
First you need to know the enemy for being prepared of what you can found on the wild jungle of Rails APPS that you may develop or someone else code. The kind of errors that could make your APP waste your precious time are:
- Database operations (The famous N+1 or missing foreign_keys on DB)
- No cache used or Memoization
- Network access (Heavy API call to external sites)
- Task that should run on background
- Yourself (For not using tools that could help you prevent the error)
Usually this kind of the errors are the ones that get us on troubles, and they are easy to miss when you are on a hurry to deliver a feature, create a hotfix, or because the lack of experience.
Prepare your secret weapons
These are some basic tools that I include on the projects I work on to check and prevent errors that could create and slow endpoints.
ACTIVE RECORD TOOLS
Accessing your database or processing a large file could be time-consuming so these are some GEMS that help us identify problems before they land on production.
1) Bullet GEM
gem 'bullet'
is one of the most popular and easiest solutions for eliminating the nasty N+1 queries. This could improve the application speed drastically. Its important that when you have a thousand of records you may not event notice it but then the table have hundreds of thousands of records this will became a serious problem.
2) Rails Panel
If you are using Google Chrome or Firefox, there is a handy extension will show you the breakdown of time taken in Rendering, ActiveRecord, Access the Cache, etc. You can find more info here.
It's important to add the gem 'meta_request'
on your Gemfile, so the data could appear in your Rails Panel after installing the extension.
3) Rack Mini Profiler
gem 'rack-mini-profiler'
after install it and reload your server you will find a little popup on your left upper corner of your page where you can click and get all the insights. Mini Profilers give you a constant reminder of how many requests have being completed for rendering the page and how long each request takes to load the full page. This help us to lean more about how our app is performing. When we are looking for this information it's important to start from where the page take long to render so you can start digging in the right direction to fix a slow view for example.
4) Active Record Query Trace
gem 'active_record_query_trace'
if you are a hard core console guy you will love this one, because if will include a super handy data like the raw SQL so you can detect slow querys on your LOGS and give you what method was call that generate those querys that are wasting your precious time.
5) Active Record Doctor
gem 'active_record_doctor'
is an easy way to detect missing unindexed foreign keys in your app. Its super common on the creation of associations like has_many thought an intermediate table some indexes of the foreign keys get missed.
KNOWLEDGE TOOLS
Not every speed improvement could be detected by a GEM, but these are other patterns that could improve your speed if are used wisely.
1) Active Record
The ORM it's an amazing tool that take a heavy load from us. But at what cost? Its important that you understand the basic and know your SQL so you don't miss the import scope to make your app faster.
2) Missing cache
Caching it's an effective way to improve speeds on you APP responses times. This is as simple as cache a JSON response on your API for example. Ruby on Rails has some built it functionally you can take advantage of. To know more about all types of caching you can implement visit this little guide.
3) Memoization
What is Memoization is a caching technique of storing a computed value to avoid duplicated work by future calls. For example your current_user or current_account. When you call your current_user you will do the hardwork to identify the record and store it for the next time you need the result that cost consuming resources.
4) Missing Background Jobs
A classic error I have found on several projects is the use of background jobs that could make your queue or request get on hold because a JOB its taking long time to execute. One thumb up rule if you need to call external API or WebSerivice try to separate your logic in different steps to get the work done.
METRICS TOOLS
The production environment always will be our best teacher. When the real users start using your APP is when you start finding that some errors could not being prevented without previous experience, but we need a way from being aware of them and being prepared to take action when they occur.
1) Datadog / New Relic
Datadog and New Relic have almost the same function, the choice is more of price and extra functionalities of each platform. This kind of systems provide with a deep information analysis of the performance of your Rails APP and how it handles to run on production, all the request data is sent to this platforms so you can monitor it on real-time.
2) Rollbar
When things go wrong, you want to know where they happen and what the user was doing. So Rollbar it's a real-time error monitoring than provide a full trace of the error so you can have a deep look to where it happens, it has a nice UI to keep track of the things have been fixed and what other errors need to be take care off.
My Steps for Debugging
These tools and knowledge can help us to create a lighting APP that we can be proud of and our clients for sure will love. Who doesn't love an APP that load in seconds? Google has found that 53% of mobile website visitors will leave if a web page doesn’t load within three seconds. THREE SECONDS! This is why it's important to improve your app performance or you can loose clients.
After getting some knowledge of what tools to use, my steps for start debugging is starting with some profiling and start looking the places where the APP slow down and could need an improvement.
- Load the VIEW and check on the console with bullet for N+1 querys that can be affecting. Also Rails Panel or Mini Profiling can give you hints for this.
- Check for long partials to render. If we are trying to improve an HTML view try to fix the longest partial and start from there how you can improve it. If we are debugging an API endpoint check how the JSON is generated, depending on the tools you work jbuilder, active model serializer or fast_jsonapi will be a different approach but all of them render partials when they nest content of associations that you can check for make improvements.
- Look for external API calls that could be placed on a background job.
- Review the HTML view or for an API JSON response if you can improve it with some caching. The most common thing I have found is that some API didn't enable the cache at all.
Conclusion
I learn the hard way… I hope this information help you to learn from somebody mistakes and could prevent your Rails APP to crash. I bet all the developers have been proud of seen their code running live, but when it start getting real traffic and hundreds of users accessing simultaneously, we all have pay the price for make the same mistakes. Talking from myself, I already pay the price for being a NOOB on the past, so please take notes and happy coding!