Ruby on Rails: Performance

November 25, 2023

Profiling

gem install stackprof
StackProf.run(mode: :cpu, raw: true, out: 'tmp/stackprof-cpu-myapp.dump') do
  #...
end
# mode: cpu, wall, object. use object for memory
gem install stackprof-webnav
stackprof-webnav -f /path/to/stackprof.dump

Active Record

Maximum Number / Calculate Needs

A released connection will be returned to the pool, but not disconnect from the database server. The connection will remain connected in the pool, waiting for a new thread to request a connection. Hence the connection will count against your database server's maximum number of connections.

Only when a connection in the pool remains unused for more than 5 minutes, ActiveRecord will actually close the connection. You can configure this by setting the idle_timeout in your database.yml.

-- Using ActiveRecord with threads might use more database connections than you think

(2 pods * 3 workers) * (1 pod * 10 sidekiq workers) = 6 * 10 = 60 connections

((2 pods * 3 workers) * (1 pod * 10 sidekiq workers)) * 5 threads = (6 * 10) * 5 = 300 connections

reaping_frequency: when to look for connections from inactive threads

ActiveRecord::Base.clear_active_connections!: clears all active connections and diconnects forcibly

ActiveRecord::Base.connection_pool.release_connection: releases the connection back to the pool without disconnecting so it can be reused

Reducing memory

  1. reduce sidekiq threads
  2. use MALLOC_ARENA_MAX=2 as an env var

Caching

rails dev:cache # will enable/disable the cache if you have the default config in rails 5+

Example cache config in development.rb

if Rails.root.join('tmp/caching-dev.txt').exist?
  config.action_controller.perform_caching = true
  config.static_cache_control = "public, max-age=172800"
  config.cache_store = :mem_cache_store
else
  config.action_controller.perform_caching = false
  config.cache_store = :null_store
end