A script to test the memory usage of your Rails application over time. It will run 30 requests against the specified action and report the final RSS. Choose the URL to hit on line 45 and then run with ruby bench_rails_memory_usage.rb
.
require "net/http"
def start_server
# Remove the X to enable the parameters for tuning.
# These are the default values as of Ruby 2.2.0.
@child = spawn(<<-EOC.split.join(" "))
XRUBY_GC_HEAP_FREE_SLOTS=4096
XRUBY_GC_HEAP_INIT_SLOTS=10000
XRUBY_GC_HEAP_GROWTH_FACTOR=1.8
XRUBY_GC_HEAP_GROWTH_MAX_SLOTS=0
XRUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=2.0
XRUBY_GC_MALLOC_LIMIT=16777216
XRUBY_GC_MALLOC_LIMIT_MAX=33554432
XRUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR=1.4
XRUBY_GC_OLDMALLOC_LIMIT=16777216
XRUBY_GC_OLDMALLOC_LIMIT_MAX=134217728
XRUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR=1.2
rails server -p3009 > /dev/null
EOC
sleep 0.1 until alive?
end
def alive?
system("curl http://localhost:3009/ &> /dev/null")
end
def stop_server
Process.kill "HUP", server_pid
Process.wait @child
end
def server_pid
`cat tmp/pids/server.pid`.to_i
end
def memory_size_mb
(`ps -o rss= -p #{server_pid}`.to_i * 1024).to_f / 2**20
end
# In /etc/hosts I have api.rails.local set to 127.0.0.1 for
# API testing on any app. Curl freaks out and takes extra
# seconds to do the request to these .local things, so we
# will use Net::HTTP for moar speed.
def do_request
uri = URI("http://api.rails.local:3009/memory_heavy_action")
req = Net::HTTP::Get.new(uri)
# Remove the next line if you don't need HTTP basic authentication.
req.basic_auth("user@example.com", "password")
req["Content-Type"] = "application/json"
Net::HTTP.start("localhost", uri.port) do |http|
http.read_timeout = 60
http.request(req)
end
end
results = []
# You can’t just measure once: memory usage has some variance.
# We will take the mean of 7 runs.
7.times do
start_server
used_mb = nil
(1..30).map do |n|
print "Request #{n}..."
do_request
used_mb = memory_size_mb
puts "#{used_mb} MB"
end
final_mb = used_mb
results << final_mb
puts "Final Memory: #{final_mb} MB"
stop_server
end
mean_final_mb = results.reduce(:+) / results.size
puts "Mean Final Memory: #{mean_final_mb} MB"