Skip to content

Commit 1ab22eb

Browse files
committed
Group all code for a tool into a single block
1 parent 24e38a5 commit 1ab22eb

File tree

1 file changed

+115
-110
lines changed

1 file changed

+115
-110
lines changed

spec/performance/bench.rb

Lines changed: 115 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,6 @@
2424
TOOLS = ENV.fetch("TOOLS", "fortio,vegeta,k6").split(",")
2525

2626
OUTDIR = "bench_results"
27-
FORTIO_JSON = "#{OUTDIR}/fortio.json".freeze
28-
FORTIO_TXT = "#{OUTDIR}/fortio.txt".freeze
29-
VEGETA_BIN = "#{OUTDIR}/vegeta.bin".freeze
30-
VEGETA_JSON = "#{OUTDIR}/vegeta.json".freeze
31-
VEGETA_TXT = "#{OUTDIR}/vegeta.txt".freeze
32-
K6_TEST_JS = "#{OUTDIR}/k6_test.js".freeze
33-
K6_SUMMARY_JSON = "#{OUTDIR}/k6_summary.json".freeze
34-
K6_TXT = "#{OUTDIR}/k6.txt".freeze
3527
SUMMARY_TXT = "#{OUTDIR}/summary.txt".freeze
3628

3729
# Validate input parameters
@@ -73,11 +65,6 @@ def parse_json_file(file_path, tool_name)
7365

7466
raise "MAX_CONNECTIONS (#{MAX_CONNECTIONS}) must be >= CONNECTIONS (#{CONNECTIONS})" if MAX_CONNECTIONS < CONNECTIONS
7567

76-
# Precompute checks for each tool
77-
run_fortio = TOOLS.include?("fortio")
78-
run_vegeta = TOOLS.include?("vegeta")
79-
run_k6 = TOOLS.include?("k6")
80-
8168
# Check required tools are installed
8269
required_tools = TOOLS + %w[column tee]
8370
required_tools.each do |cmd|
@@ -126,103 +113,43 @@ def server_responding?(uri)
126113

127114
FileUtils.mkdir_p(OUTDIR)
128115

129-
# Configure tool-specific arguments
130-
if RATE == "max"
131-
if CONNECTIONS != MAX_CONNECTIONS
132-
raise "For RATE=max, CONNECTIONS must be equal to MAX_CONNECTIONS (got #{CONNECTIONS} and #{MAX_CONNECTIONS})"
133-
end
134-
135-
fortio_args = ["-qps", 0, "-c", CONNECTIONS]
136-
vegeta_args = ["-rate=infinity", "--workers=#{CONNECTIONS}", "--max-workers=#{CONNECTIONS}"]
137-
k6_scenarios = <<~JS.strip
138-
{
139-
max_rate: {
140-
executor: 'constant-vus',
141-
vus: #{CONNECTIONS},
142-
duration: '#{DURATION}'
143-
}
144-
}
145-
JS
146-
else
147-
fortio_args = ["-qps", RATE, "-uniform", "-nocatchup", "-c", CONNECTIONS]
148-
vegeta_args = ["-rate=#{RATE}", "--workers=#{CONNECTIONS}", "--max-workers=#{MAX_CONNECTIONS}"]
149-
k6_scenarios = <<~JS.strip
150-
{
151-
constant_rate: {
152-
executor: 'constant-arrival-rate',
153-
rate: #{RATE},
154-
timeUnit: '1s',
155-
duration: '#{DURATION}',
156-
preAllocatedVUs: #{CONNECTIONS},
157-
maxVUs: #{MAX_CONNECTIONS}
158-
}
159-
}
160-
JS
161-
end
162-
163-
# Run Fortio
164-
if run_fortio
165-
puts "===> Fortio"
166-
# TODO: https://github.com/fortio/fortio/wiki/FAQ#i-want-to-get-the-best-results-what-flags-should-i-pass
167-
fortio_cmd = [
168-
"fortio", "load",
169-
*fortio_args,
170-
"-t", DURATION,
171-
"-timeout", REQUEST_TIMEOUT,
172-
"-json", FORTIO_JSON,
173-
TARGET
174-
].join(" ")
175-
raise "Fortio benchmark failed" unless system("#{fortio_cmd} | tee #{FORTIO_TXT}")
116+
# Validate RATE=max constraint
117+
is_max_rate = RATE == "max"
118+
if is_max_rate && CONNECTIONS != MAX_CONNECTIONS
119+
raise "For RATE=max, CONNECTIONS must be equal to MAX_CONNECTIONS (got #{CONNECTIONS} and #{MAX_CONNECTIONS})"
176120
end
177121

178-
# Run Vegeta
179-
if run_vegeta
180-
puts "\n===> Vegeta"
181-
vegeta_cmd = [
182-
"echo", "'GET #{TARGET}'", "|",
183-
"vegeta", "attack",
184-
*vegeta_args,
185-
"-duration=#{DURATION}",
186-
"-timeout=#{REQUEST_TIMEOUT}"
187-
].join(" ")
188-
raise "Vegeta attack failed" unless system("#{vegeta_cmd} | tee #{VEGETA_BIN} | vegeta report | tee #{VEGETA_TXT}")
189-
raise "Vegeta report generation failed" unless system("vegeta report -type=json #{VEGETA_BIN} > #{VEGETA_JSON}")
190-
end
191-
192-
# Run k6
193-
if run_k6
194-
puts "\n===> k6"
195-
k6_script = <<~JS
196-
import http from 'k6/http';
197-
import { check } from 'k6';
198-
199-
export const options = {
200-
scenarios: #{k6_scenarios},
201-
};
202-
203-
export default function () {
204-
const response = http.get('#{TARGET}', { timeout: '#{REQUEST_TIMEOUT}' });
205-
check(response, {
206-
'status=200': r => r.status === 200,
207-
// you can add more if needed:
208-
// 'status=500': r => r.status === 500,
209-
});
210-
}
211-
JS
212-
File.write(K6_TEST_JS, k6_script)
213-
k6_command = "k6 run --summary-export=#{K6_SUMMARY_JSON} --summary-trend-stats 'min,avg,med,max,p(90),p(99)'"
214-
raise "k6 benchmark failed" unless system("#{k6_command} #{K6_TEST_JS} | tee #{K6_TXT}")
215-
end
216-
217-
puts "\n===> Parsing results and generating summary"
218-
219122
# Initialize summary file
220123
File.write(SUMMARY_TXT, "Tool\tRPS\tp50(ms)\tp90(ms)\tp99(ms)\tStatus\n")
221124

222-
# Parse Fortio results
223-
if run_fortio
125+
# Fortio
126+
if TOOLS.include?("fortio")
224127
begin
225-
fortio_data = parse_json_file(FORTIO_JSON, "Fortio")
128+
puts "===> Fortio"
129+
130+
fortio_json = "#{OUTDIR}/fortio.json"
131+
fortio_txt = "#{OUTDIR}/fortio.txt"
132+
133+
# Configure Fortio arguments
134+
# See https://github.com/fortio/fortio/wiki/FAQ#i-want-to-get-the-best-results-what-flags-should-i-pass
135+
fortio_args =
136+
if is_max_rate
137+
["-qps", 0, "-c", CONNECTIONS]
138+
else
139+
["-qps", RATE, "-uniform", "-nocatchup", "-c", CONNECTIONS]
140+
end
141+
142+
fortio_cmd = [
143+
"fortio", "load",
144+
*fortio_args,
145+
"-t", DURATION,
146+
"-timeout", REQUEST_TIMEOUT,
147+
"-json", fortio_json,
148+
TARGET
149+
].join(" ")
150+
raise "Fortio benchmark failed" unless system("#{fortio_cmd} | tee #{fortio_txt}")
151+
152+
fortio_data = parse_json_file(fortio_json, "Fortio")
226153
fortio_rps = fortio_data["ActualQPS"]&.round(2) || "missing"
227154

228155
percentiles = fortio_data.dig("DurationHistogram", "Percentiles") || []
@@ -247,10 +174,34 @@ def server_responding?(uri)
247174
end
248175
end
249176

250-
# Parse Vegeta results
251-
if run_vegeta
177+
# Vegeta
178+
if TOOLS.include?("vegeta")
252179
begin
253-
vegeta_data = parse_json_file(VEGETA_JSON, "Vegeta")
180+
puts "\n===> Vegeta"
181+
182+
vegeta_bin = "#{OUTDIR}/vegeta.bin"
183+
vegeta_json = "#{OUTDIR}/vegeta.json"
184+
vegeta_txt = "#{OUTDIR}/vegeta.txt"
185+
186+
# Configure Vegeta arguments
187+
vegeta_args =
188+
if is_max_rate
189+
["-rate=infinity", "--workers=#{CONNECTIONS}", "--max-workers=#{CONNECTIONS}"]
190+
else
191+
["-rate=#{RATE}", "--workers=#{CONNECTIONS}", "--max-workers=#{MAX_CONNECTIONS}"]
192+
end
193+
194+
vegeta_cmd = [
195+
"echo 'GET #{TARGET}' |",
196+
"vegeta", "attack",
197+
*vegeta_args,
198+
"-duration=#{DURATION}",
199+
"-timeout=#{REQUEST_TIMEOUT}"
200+
].join(" ")
201+
raise "Vegeta attack failed" unless system("#{vegeta_cmd} | tee #{vegeta_bin} | vegeta report | tee #{vegeta_txt}")
202+
raise "Vegeta report generation failed" unless system("vegeta report -type=json #{vegeta_bin} > #{vegeta_json}")
203+
204+
vegeta_data = parse_json_file(vegeta_json, "Vegeta")
254205
# .throughput is successful_reqs/total_period, .rate is all_requests/attack_period
255206
vegeta_rps = vegeta_data["throughput"]&.round(2) || "missing"
256207
vegeta_p50 = vegeta_data.dig("latencies", "50th")&./(1_000_000.0)&.round(2) || "missing"
@@ -271,10 +222,64 @@ def server_responding?(uri)
271222
end
272223
end
273224

274-
# Parse k6 results
275-
if run_k6
225+
# k6
226+
if TOOLS.include?("k6")
276227
begin
277-
k6_data = parse_json_file(K6_SUMMARY_JSON, "k6")
228+
puts "\n===> k6"
229+
230+
k6_script_file = "#{OUTDIR}/k6_test.js"
231+
k6_summary_json = "#{OUTDIR}/k6_summary.json"
232+
k6_txt = "#{OUTDIR}/k6.txt"
233+
234+
# Configure k6 scenarios
235+
k6_scenarios =
236+
if is_max_rate
237+
<<~JS.strip
238+
{
239+
max_rate: {
240+
executor: 'constant-vus',
241+
vus: #{CONNECTIONS},
242+
duration: '#{DURATION}'
243+
}
244+
}
245+
JS
246+
else
247+
<<~JS.strip
248+
{
249+
constant_rate: {
250+
executor: 'constant-arrival-rate',
251+
rate: #{RATE},
252+
timeUnit: '1s',
253+
duration: '#{DURATION}',
254+
preAllocatedVUs: #{CONNECTIONS},
255+
maxVUs: #{MAX_CONNECTIONS}
256+
}
257+
}
258+
JS
259+
end
260+
261+
k6_script = <<~JS
262+
import http from 'k6/http';
263+
import { check } from 'k6';
264+
265+
export const options = {
266+
scenarios: #{k6_scenarios},
267+
};
268+
269+
export default function () {
270+
const response = http.get('#{TARGET}', { timeout: '#{REQUEST_TIMEOUT}' });
271+
check(response, {
272+
'status=200': r => r.status === 200,
273+
// you can add more if needed:
274+
// 'status=500': r => r.status === 500,
275+
});
276+
}
277+
JS
278+
File.write(k6_script_file, k6_script)
279+
k6_command = "k6 run --summary-export=#{k6_summary_json} --summary-trend-stats 'min,avg,med,max,p(90),p(99)'"
280+
raise "k6 benchmark failed" unless system("#{k6_command} #{k6_script_file} | tee #{k6_txt}")
281+
282+
k6_data = parse_json_file(k6_summary_json, "k6")
278283
k6_rps = k6_data.dig("metrics", "iterations", "rate")&.round(2) || "missing"
279284
k6_p50 = k6_data.dig("metrics", "http_req_duration", "med")&.round(2) || "missing"
280285
k6_p90 = k6_data.dig("metrics", "http_req_duration", "p(90)")&.round(2) || "missing"

0 commit comments

Comments
 (0)