New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
benchmarks - wrk overload testing files [ci skip] #2695
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,229 @@ | ||||
# frozen_string_literal: true | ||||
|
||||
require 'optparse' | ||||
|
||||
module TestPuma | ||||
class BenchBase | ||||
|
||||
# Signal used to trigger worker GC | ||||
GC_SIGNAL = 'SIGALRM' | ||||
|
||||
# We're running under GitHub Actions | ||||
IS_GHA = ENV['GITHUB_ACTIONS'] == 'true' | ||||
|
||||
WRK_PERCENTILE = [0.50, 0.75, 0.9, 0.99, 1.0].freeze | ||||
|
||||
def initialize | ||||
sleep 5 # wait for server to boot | ||||
|
||||
@thread_loops = nil | ||||
@clients_per_thread = nil | ||||
@req_per_client = nil | ||||
@body_kb = 10 | ||||
@dly_app = nil | ||||
@bind_type = :tcp | ||||
|
||||
@ios_to_close = [] | ||||
|
||||
setup_options | ||||
|
||||
unless File.exist? @state_file | ||||
puts "can't fined state file '#{@state_file}'" | ||||
exit 1 | ||||
end | ||||
|
||||
mstr_pid = File.binread(@state_file)[/^pid: +(\d+)/, 1].to_i | ||||
begin | ||||
Process.kill 0, mstr_pid | ||||
rescue Errno::ESRCH | ||||
puts 'Puma server stopped?' | ||||
exit 1 | ||||
rescue Errno::EPERM | ||||
end | ||||
|
||||
case @bind_type | ||||
when :ssl, :tcp | ||||
@bind_port = ENV.fetch('PORT', 40010).to_i | ||||
when :unix | ||||
@bind_path = "#{Dir.home}/skt.unix" | ||||
when :aunix | ||||
@bind_path = "@skt.aunix" | ||||
else | ||||
exit 1 | ||||
end | ||||
end | ||||
|
||||
def setup_options | ||||
OptionParser.new do |o| | ||||
o.on "-l", "--loops LOOPS", OptionParser::DecimalInteger, "create_clients: loops/threads" do |arg| | ||||
@thread_loops = arg.to_i | ||||
end | ||||
|
||||
o.on "-c", "--connections CONNECTIONS", OptionParser::DecimalInteger, "create_clients: clients_per_thread" do |arg| | ||||
@clients_per_thread = arg.to_i | ||||
end | ||||
|
||||
o.on "-r", "--requests REQUESTS", OptionParser::DecimalInteger, "create_clients: requests per client" do |arg| | ||||
@req_per_client = arg.to_i | ||||
end | ||||
|
||||
o.on "-b", "--body_kb BODYKB", OptionParser::DecimalInteger, "CI RackUp: size of response body in kB" do |arg| | ||||
@body_kb = arg.to_i | ||||
end | ||||
|
||||
o.on "-d", "--dly_app DELAYAPP", Float, "CI RackUp: app response delay" do |arg| | ||||
@dly_app = arg.to_f | ||||
end | ||||
|
||||
o.on "-s", "--socket SOCKETTYPE", String, "Bind type: tcp, ssl, unix, aunix" do |arg| | ||||
@bind_type = arg.to_sym | ||||
end | ||||
|
||||
o.on "-S", "--state STATEFILE", String, "Puma Server: state file" do |arg| | ||||
@state_file = arg | ||||
end | ||||
|
||||
o.on "-t", "--threads THREADS", String, "Puma Server: threads" do |arg| | ||||
@threads = arg[/\d+\z/].to_i | ||||
end | ||||
|
||||
o.on "-w", "--workers WORKERS", OptionParser::DecimalInteger, "Puma Server: workers" do |arg| | ||||
@workers = arg.to_i | ||||
end | ||||
|
||||
o.on "-T", "--time TIME", OptionParser::DecimalInteger, "wrk: duration" do |arg| | ||||
@wrk_time = arg.to_i | ||||
end | ||||
|
||||
o.on "-W", "--wrk_bind WRK_STR", String, "wrk: bind string" do |arg| | ||||
@wrk_bind_str = arg | ||||
end | ||||
|
||||
o.on("-h", "--help", "Prints this help") do | ||||
puts o | ||||
exit | ||||
end | ||||
end.parse! ARGV | ||||
end | ||||
|
||||
def close_clients | ||||
closed = 0 | ||||
@ios_to_close.each do |socket| | ||||
if socket && socket.to_io.is_a?(IO) && !socket.closed? | ||||
begin | ||||
if @bind_type == :ssl | ||||
socket.sysclose | ||||
else | ||||
socket.close | ||||
end | ||||
closed += 1 | ||||
rescue Errno::EBADF | ||||
end | ||||
end | ||||
end | ||||
puts "Closed #{closed} clients" unless closed.zero? | ||||
end | ||||
|
||||
def run_wrk_parse(cmd) | ||||
print cmd.ljust 55 | ||||
|
||||
wrk_output = %x[#{cmd}] | ||||
|
||||
wrk_data = "#{wrk_output[/\A.+ connections/m]}\n#{wrk_output[/ Thread Stats.+\z/m]}" | ||||
|
||||
print " |#{wrk_data[/^ +\d+ +requests.+/].rstrip}\n" | ||||
|
||||
# puts '', wrk_data, '' # for debugging or output format changes | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be removed or uncommented?
Suggested change
|
||||
|
||||
hsh = {} | ||||
|
||||
hsh[:rps] = wrk_data[/^Requests\/sec: +([\d.]+)/, 1].to_f.round | ||||
hsh[:requests] = wrk_data[/^ +(\d+) +requests/, 1].to_i | ||||
if (t = wrk_data[/^ +Socket errors: +(.+)/, 1]) | ||||
hsh[:errors] = t | ||||
end | ||||
|
||||
read = wrk_data[/ +([\d.]+)(GB|KB|MB) +read$/, 1].to_f | ||||
unit = wrk_data[/ +[\d.]+(GB|KB|MB) +read$/, 1] | ||||
|
||||
mult = | ||||
case unit | ||||
when 'KB' then 1_024 | ||||
when 'MB' then 1_024**2 | ||||
when 'GB' then 1_024**3 | ||||
end | ||||
|
||||
hsh[:read] = (mult * read).round | ||||
|
||||
if hsh[:errors] | ||||
t = hsh[:errors] | ||||
hsh[:errors] = t.sub('connect ', 'c').sub('read ', 'r') | ||||
.sub('write ', 'w').sub('timeout ', 't') | ||||
end | ||||
|
||||
t_re = ' +([\d.ums]+)' | ||||
|
||||
latency = | ||||
wrk_data.match(/^ +50%#{t_re}\s+75%#{t_re}\s+90%#{t_re}\s+99%#{t_re}/).captures | ||||
# add up max time | ||||
latency.push wrk_data[/^ +Latency.+/].split(' ')[-2] | ||||
|
||||
hsh[:times] = WRK_PERCENTILE.zip(latency.map do |t| | ||||
if t.end_with?('ms') | ||||
t.to_f | ||||
elsif t.end_with?('us') | ||||
t.to_f/1000 | ||||
elsif t.end_with?('s') | ||||
t.to_f * 1000 | ||||
else | ||||
0 | ||||
end | ||||
end).to_h | ||||
hsh | ||||
end | ||||
|
||||
def parse_stats | ||||
obj = @puma_info.run 'stats' | ||||
# puts ''; pp obj; puts '' | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above!
Suggested change
|
||||
wrks = obj[:worker_status] | ||||
stats = {} | ||||
wrks.each do |w| | ||||
pid = w[:pid] | ||||
req_cnt = w[:last_status][:requests_count] | ||||
id = format 'worker-%01d-%02d', w[:phase], w[:index] | ||||
hsh = { | ||||
pid: w[:pid], | ||||
requests: req_cnt - @worker_req_ttl[w[:pid]], | ||||
backlog: w[:last_status][:backlog] | ||||
} | ||||
@pids[hsh[:pid]] = id | ||||
@worker_req_ttl[pid] = req_cnt | ||||
stats[id] = hsh | ||||
end | ||||
stats | ||||
end | ||||
|
||||
def parse_smem | ||||
@puma_info.run 'gc' | ||||
sleep 1 | ||||
|
||||
hsh_smem = Hash.new [] | ||||
pids = @pids.keys | ||||
|
||||
smem_info = %x[smem -c 'pid rss pss uss command'] | ||||
# puts '', smem_info, '' | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above!
Suggested change
|
||||
smem_info.lines.each do |l| | ||||
ary = l.strip.split ' ', 5 | ||||
if pids.include? ary[0].to_i | ||||
hsh_smem[@pids[ary[0].to_i]] = { | ||||
pid: ary[0].to_i, | ||||
rss: ary[1].to_i, | ||||
pss: ary[2].to_i, | ||||
uss: ary[3].to_i | ||||
} | ||||
end | ||||
end | ||||
hsh_smem.sort.to_h | ||||
end | ||||
end | ||||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
#!/bin/bash | ||
|
||
# -l client threads (loops) | ||
# -c connections per client thread | ||
# -r requests per client | ||
# | ||
# Total connections/requests = l * c * r | ||
# | ||
# -b response body size kB | ||
# -d app delay | ||
# | ||
# -s Puma bind socket type, default ssl, also tcp or unix | ||
# -t Puma threads, default 5:5 | ||
# -w Puma workers, default 2 | ||
# -r Puma rackup file | ||
|
||
|
||
export HOST=127.0.0.1 | ||
export PORT=40001 | ||
export CTRL=40010 | ||
export STATE=tmp/bench_test_puma.state | ||
|
||
while getopts l:C:c:d:r:R:s:b:T:t:w: option | ||
do | ||
case "${option}" | ||
in | ||
#———————————————————— create_clients options | ||
l) loops=${OPTARG};; | ||
c) connections=${OPTARG};; | ||
r) req_per_client=${OPTARG};; | ||
#———————————————————— Puma options | ||
C) conf=${OPTARG};; | ||
t) threads=${OPTARG};; | ||
w) workers=${OPTARG};; | ||
R) rackup_file=${OPTARG};; | ||
#———————————————————— app/common options | ||
b) body_kb=${OPTARG};; | ||
s) skt_type=${OPTARG};; | ||
d) dly_app=${OPTARG};; | ||
#———————————————————— wrk options | ||
# c (connections) is also used for wrk | ||
T) time=${OPTARG};; | ||
esac | ||
done | ||
|
||
optional_args="-S $STATE" | ||
|
||
if [ -z "$loops" ] ; then | ||
loops=10 | ||
fi | ||
|
||
if [ -z "$connections" ]; then | ||
connections=0 | ||
fi | ||
|
||
if [ -z "$time" ] ; then | ||
time=15 | ||
fi | ||
|
||
if [ -z "$req_per_client" ]; then | ||
req_per_client=1 | ||
fi | ||
|
||
if [ -n "$dly_app" ]; then | ||
optional_args="$optional_args -d$dly_app" | ||
fi | ||
|
||
if [ -n "$body_kb" ]; then | ||
optional_args="$optional_args -b$body_kb" | ||
export CI_TEST_KB=$body_kb | ||
fi | ||
|
||
if [ -z "$skt_type" ]; then | ||
skt_type=tcp | ||
fi | ||
|
||
puma_args="-S $STATE" | ||
|
||
if [ -n "$workers" ]; then | ||
puma_args="$puma_args -w$workers" | ||
fi | ||
|
||
if [ -n "$threads" ]; then | ||
puma_args="$puma_args -t$threads" | ||
fi | ||
|
||
if [ -n "$conf" ]; then | ||
puma_args="$puma_args -C $conf" | ||
fi | ||
|
||
if [ -z "$rackup_file" ]; then | ||
rackup_file="test/rackup/ci_string.ru" | ||
fi | ||
|
||
case $skt_type in | ||
ssl) | ||
bind="ssl://$HOST:$PORT?cert=examples/puma/cert_puma.pem&key=examples/puma/puma_keypair.pem&verify_mode=none" | ||
curl_str=https://$HOST:$PORT | ||
wrk_str=https://$HOST:$PORT | ||
;; | ||
tcp) | ||
bind=tcp://$HOST:$PORT | ||
curl_str=http://$HOST:$PORT | ||
wrk_str=http://$HOST:$PORT | ||
;; | ||
unix) | ||
bind=unix://$HOME/skt.unix | ||
curl_str="--unix-socket $HOME/skt.unix http:/n" | ||
wrk_str=unix://$HOME/skt.unix | ||
;; | ||
aunix) | ||
bind=unix://@skt.aunix | ||
curl_str="--abstract-unix-socket skt.aunix http:/n" | ||
wrk_str=unix://@skt.aunix | ||
;; | ||
esac |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed a small typo here! 🙈