Skip to content

Commit dcfd72c

Browse files
authored
ci: add stackprof (#289)
1 parent c1d704d commit dcfd72c

File tree

6 files changed

+111
-28
lines changed

6 files changed

+111
-28
lines changed

.github/workflows/test.yaml

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,36 @@ jobs:
201201
do
202202
docker compose -f $DOCKER_COMPOSE_FILE exec node$i tc qdisc del dev eth0 root netem || true
203203
done
204+
- name: Stop containers
205+
run: docker compose -f $DOCKER_COMPOSE_FILE down || true
206+
ips:
207+
name: IPS
208+
timeout-minutes: 10
209+
runs-on: ubuntu-latest
210+
env:
211+
REDIS_VERSION: '7.2'
212+
DOCKER_COMPOSE_FILE: 'compose.latency.yaml'
213+
REDIS_REPLICA_SIZE: '2'
214+
REDIS_CLIENT_MAX_THREADS: '10'
215+
DELAY_TIME: '0ms'
216+
steps:
217+
- name: Check out code
218+
uses: actions/checkout@v4
219+
- name: Set up Ruby
220+
uses: ruby/setup-ruby@v1
221+
with:
222+
ruby-version: '3.2'
223+
bundler-cache: true
224+
- name: Pull Docker images
225+
run: docker pull redis:$REDIS_VERSION
226+
- name: Run containers
227+
run: docker compose -f $DOCKER_COMPOSE_FILE up -d
228+
- name: Wait for Redis cluster to be ready
229+
run: bundle exec rake wait
230+
- name: Print containers
231+
run: docker compose -f $DOCKER_COMPOSE_FILE ps
232+
- name: Print cpu info
233+
run: grep 'model name' /proc/cpuinfo
204234
- name: Run iteration per second
205235
run: bundle exec rake ips
206236
- name: Stop containers
@@ -235,10 +265,10 @@ jobs:
235265
run: bundle exec rake wait
236266
- name: Print containers
237267
run: docker compose -f $DOCKER_COMPOSE_FILE ps
238-
- name: Run memory profiler
268+
- name: Run profiler
239269
run: bundle exec rake prof
240270
env:
241-
MEMORY_PROFILE_MODE: ${{ matrix.mode }}
271+
PROFILE_MODE: ${{ matrix.mode }}
242272
- name: Stop containers
243273
run: docker compose -f $DOCKER_COMPOSE_FILE down || true
244274
massive:
@@ -295,9 +325,9 @@ jobs:
295325
DEBUG: '1'
296326
- name: Print containers
297327
run: docker compose -f $DOCKER_COMPOSE_FILE ps
298-
- name: Run memory profiler
328+
- name: Run profiler
299329
run: bundle exec rake prof
300330
env:
301-
MEMORY_PROFILE_MODE: pipelining_in_moderation
331+
PROFILE_MODE: pipelining_in_moderation
302332
- name: Stop containers
303333
run: docker compose -f $DOCKER_COMPOSE_FILE down || true

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ gem 'rubocop'
1212
gem 'rubocop-minitest', require: false
1313
gem 'rubocop-performance', require: false
1414
gem 'rubocop-rake', require: false
15+
gem 'stackprof', platform: :mri

Rakefile

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,28 +29,14 @@ SLUGGISH_TEST_TYPES.each do |type|
2929
end
3030
end
3131

32-
Rake::TestTask.new(:bench) do |t|
33-
t.libs << :lib
34-
t.libs << :test
35-
t.options = '-v'
36-
t.warning = false
37-
t.test_files = ARGV.size > 1 ? ARGV[1..] : Dir['test/**/bench_*.rb']
38-
end
39-
40-
Rake::TestTask.new(:ips) do |t|
41-
t.libs << :lib
42-
t.libs << :test
43-
t.options = '-v'
44-
t.warning = false
45-
t.test_files = ARGV.size > 1 ? ARGV[1..] : Dir['test/**/ips_*.rb']
46-
end
47-
48-
Rake::TestTask.new(:prof) do |t|
49-
t.libs << :lib
50-
t.libs << :test
51-
t.options = '-v'
52-
t.warning = false
53-
t.test_files = ARGV.size > 1 ? ARGV[1..] : Dir['test/**/prof_*.rb']
32+
%i[bench ips prof].each do |k|
33+
Rake::TestTask.new(k) do |t|
34+
t.libs << :lib
35+
t.libs << :test
36+
t.options = '-v'
37+
t.warning = false
38+
t.test_files = ARGV.size > 1 ? ARGV[1..] : Dir["test/**/#{k}_*.rb"]
39+
end
5440
end
5541

5642
desc 'Wait for cluster to be ready'

compose.latency.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ services:
3737
bash -c "apt-get update > /dev/null
3838
&& apt-get install --no-install-recommends --no-install-suggests -y iproute2 iputils-ping > /dev/null
3939
&& rm -rf /var/lib/apt/lists/*
40-
&& (tc qdisc add dev eth0 root netem delay ${DELAY_TIME:-20ms} || echo skipped)
40+
&& (tc qdisc add dev eth0 root netem delay ${DELAY_TIME:-1ms} || echo skipped)
4141
&& redis-server
4242
--maxmemory 64mb
4343
--maxmemory-policy allkeys-lru

test/prof_mem.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ module ProfMem
4242
}.freeze
4343

4444
def run
45-
mode = ENV.fetch('MEMORY_PROFILE_MODE', :single).to_sym
45+
mode = ENV.fetch('PROFILE_MODE', :single).to_sym
4646
subject = MODES.fetch(mode)
4747

4848
CLI_TYPES.each do |cli_type|

test/prof_stack.rb

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# frozen_string_literal: true
2+
3+
require 'json'
4+
require 'tmpdir'
5+
require 'stackprof'
6+
require 'redis_cluster_client'
7+
require 'testing_constants'
8+
9+
module ProfStack
10+
SIZE = 40
11+
ATTEMPTS = 1000
12+
13+
module_function
14+
15+
def run
16+
client = make_client
17+
mode = ENV.fetch('PROFILE_MODE', :single).to_sym
18+
prepare(client)
19+
profile = StackProf.run(mode: :cpu, raw: true) { execute(client, mode) }
20+
StackProf::Report.new(profile).print_text(false, 40)
21+
end
22+
23+
def make_client
24+
::RedisClient.cluster(
25+
nodes: TEST_NODE_URIS,
26+
replica: true,
27+
replica_affinity: :random,
28+
fixed_hostname: TEST_FIXED_HOSTNAME,
29+
**TEST_GENERIC_OPTIONS
30+
).new_client
31+
end
32+
33+
def prepare(client)
34+
ATTEMPTS.times do |i|
35+
client.pipelined do |pi|
36+
SIZE.times do |j|
37+
n = SIZE * i + j
38+
pi.call('SET', "key#{n}", "val#{n}")
39+
end
40+
end
41+
end
42+
end
43+
44+
def execute(client, mode)
45+
case mode
46+
when :single
47+
(ATTEMPTS * SIZE).times { |i| client.call('GET', "key#{i}") }
48+
when :excessive_pipelining
49+
client.pipelined do |pi|
50+
(ATTEMPTS * SIZE).times { |i| pi.call('GET', "key#{i}") }
51+
end
52+
when :pipelining_in_moderation
53+
ATTEMPTS.times do |i|
54+
client.pipelined do |pi|
55+
SIZE.times do |j|
56+
n = SIZE * i + j
57+
pi.call('GET', "key#{n}")
58+
end
59+
end
60+
end
61+
else raise ArgumentError, mode
62+
end
63+
end
64+
end
65+
66+
ProfStack.run

0 commit comments

Comments
 (0)