Rails Performance

Apr 8, 2022 8:20 AM
Ruby on Rails

These are standards that Rails applications should have in terms of performance. Sometimes, the nature of the application might mean that you need to perform at a level not in accordance by the standards, but there must be a very strong reason for this.

  • Response times: less than 300 milliseconds on average, 1-second 95th percentile. This one is somewhat self-explanatory. Response times that are higher than this can impact the customer experience of your site. They also cost you money by requiring more hardware to achieve the same requests-per-second throughput.
  • Total server costs: less than 1 dollar per month per request-per-minute served by the app. e.g. if you’re serving 1000 requests per minute, you should be spending less than $1000 dollars per month. This is a rough rule of thumb based on my experiences with Heroku, but it is just as easy to spend as much on platforms like AWS.
  • Memory: Less than 1GB per process. Memory is one of the two resources we must balance when trying to get as much efficiency as possible out of our servers - if we run out of memory, things get very slow!
  • CPU: Utilization less than 80%, load metric equal to about 80% of the number of cores on the machine or less. For more about the load metric on why it’s so useful, see here.
  • Number of hosts per web server: Each web server (or dyno, or VPS) should have at least 4 processes (Puma workers, Unicorn workers, etc) running on it. This greatly improves routing performance and reduces time spent queuing for a Puma/Unicorn/Passenger process. I’ll be writing more about this rule in the future.
  • Cache hit rate: Cache hit rates of all kinds - database, Memcache, etc - should be 95% or higher. If cache hit rates are lower, you’re just adding latency to a cold cache situation. Keep them nice and high. Low cache hit rates are caused by poor caching strategies, usually, overly complex cache key names or a poor understanding of the read/write characteristics of the data being cached.
  • Page download size: Your pages should be about 2MB or less on initial download/first visit. Higher page weights make your page unusable on poor connections, such as cellular data or developing-world WiFi.
  • DB query speed: From the perspective of the application, most queries should take 10ms or less. Higher than this means that you either have a query performance issue (which will show up in your database metrics as collected by the DB) or a network issue (if queries look fast from the DB’s perspective but slow from the application’s perspective).
  • Rails and Ruby versions: You should be at most 1 major version behind the current Rails version, and two minor versions behind Ruby (e.g. today, Rails 6.0 and Ruby 2.6 are the most current. So, you should be at worst on Rails 5.2 and Ruby 2.4). This is usually the same as their maintenance policy, in any case. Being behind on this metric shows you are not investing in your engineering resources enough.
  • Test speed: Tests should run at 100 assertions per second (RSpec: examples per second) or faster. Fast tests are useful tests. If you’re slower than this, 99% of the time the problem is in your usage of the database and/or FactoryBot.
  • Memory internals: Ruby apps should run at 75,000 objects allocated per transaction or fewer. Most apps should run less than 20 minor garbage collection cycles per 100 transactions, and less than 3 major GCs per 100 transactions. Heap sizes should be 1.5 million objects or less. Numbers higher than this usually indicate problems with N+1s/ActiveRecord looking up too many rows at once.