Skip to content

Commit bdc584b

Browse files
ChrisBrdkeegan-figma
authored andcommitted
Reduce Redis key size
1 parent 832415b commit bdc584b

File tree

7 files changed

+184
-6
lines changed

7 files changed

+184
-6
lines changed

ruby/lib/ci/queue/redis.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
require 'ci/queue/redis/test_time_record'
1313
require 'ci/queue/redis/test_duration_moving_averages'
1414
require 'ci/queue/redis/update_test_duration_moving_average'
15+
require 'ci/queue/redis/key_shortener'
1516

1617
module CI
1718
module Queue

ruby/lib/ci/queue/redis/base.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def master_worker_id
102102
attr_reader :redis, :redis_url
103103

104104
def key(*args)
105-
['build', build_id, *args].join(':')
105+
KeyShortener.key(config.build_id, *args)
106106
end
107107

108108
def build_id

ruby/lib/ci/queue/redis/build_record.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def record_stats(stats, pipeline: redis)
124124
end
125125

126126
def key(*args)
127-
['build', config.build_id, *args].join(':')
127+
KeyShortener.key(config.build_id, *args)
128128
end
129129
end
130130
end

ruby/lib/ci/queue/redis/grind_record.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def pop_warnings
5252
attr_reader :redis, :config
5353

5454
def key(*args)
55-
['build', config.build_id, *args].join(':')
55+
KeyShortener.key(config.build_id, *args)
5656
end
5757

5858
def record_stats(stats, pipeline: redis)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# frozen_string_literal: true
2+
require 'digest/md5'
3+
4+
module CI
5+
module Queue
6+
module Redis
7+
module KeyShortener
8+
# Suffix mapping for common key patterns
9+
SUFFIX_ALIASES = {
10+
'running' => 'r',
11+
'processed' => 'p',
12+
'queue' => 'q',
13+
'owners' => 'o',
14+
'error-reports' => 'e',
15+
'requeues-count' => 'rc',
16+
'assertions' => 'a',
17+
'errors' => 'er',
18+
'failures' => 'f',
19+
'skips' => 's',
20+
'requeues' => 'rq',
21+
'total_time' => 't',
22+
'test_failed_count' => 'fc',
23+
'completed' => 'c',
24+
'master-status' => 'm',
25+
'created-at' => 'ca',
26+
'workers' => 'w',
27+
'worker' => 'w',
28+
'warnings' => 'wn',
29+
'worker-errors' => 'we',
30+
'flaky-reports' => 'fl',
31+
}.freeze
32+
33+
# We're transforming the key to a shorter format to minimize network traffic.
34+
#
35+
# Strategy:
36+
# - Shorten prefix: 'b' instead of 'build'
37+
# - Hash UUID: 8-char MD5 instead of 36-char UUID
38+
# - Alias suffixes: single letters instead of full words
39+
#
40+
# Example:
41+
# build:unit:019aef0e-c010-433e-b706-c658d3c16372:running (55 bytes)
42+
# -> b:f03d3bef:r (13 bytes, 76% reduction)
43+
44+
def self.key(build_id, *args)
45+
digest = Digest::MD5.hexdigest(build_id)[0..7]
46+
shortened_args = args.map { |arg| SUFFIX_ALIASES[arg] || arg }
47+
48+
['b', digest, *shortened_args].join(':')
49+
end
50+
end
51+
end
52+
end
53+
end

ruby/test/ci/queue/redis_test.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,9 @@ def test_acknowledge_returns_false_if_the_test_was_picked_up_by_another_worker
203203
end
204204

205205
def test_workers_register
206-
assert_equal 1, @redis.scard('build:42:workers')
206+
assert_equal 1, @redis.scard(CI::Queue::Redis::KeyShortener.key('42', 'workers'))
207207
worker(2)
208-
assert_equal 2, @redis.scard('build:42:workers')
208+
assert_equal 2, @redis.scard(CI::Queue::Redis::KeyShortener.key('42', 'workers'))
209209
end
210210

211211
def test_timeout_warning

ruby/test/integration/minitest_redis_test.rb

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,130 @@ def setup
2222
@exe = File.expand_path('../../../exe/minitest-queue', __FILE__)
2323
end
2424

25+
<<<<<<< HEAD
26+
=======
27+
def test_default_reporter
28+
out, err = capture_subprocess_io do
29+
system(
30+
{ 'BUILDKITE' => '1' },
31+
@exe, 'run',
32+
'--queue', @redis_url,
33+
'--seed', 'foobar',
34+
'--build', '1',
35+
'--worker', '1',
36+
'--timeout', '1',
37+
'--max-requeues', '1',
38+
'--requeue-tolerance', '1',
39+
'-Itest',
40+
'test/dummy_test.rb',
41+
chdir: 'test/fixtures/',
42+
)
43+
end
44+
45+
assert_empty err
46+
assert_match(/Expected false to be truthy/, normalize(out)) # failure output
47+
result = normalize(out.lines.last.strip)
48+
assert_equal '--- Ran 11 tests, 8 assertions, 2 failures, 1 errors, 1 skips, 4 requeues in X.XXs', result
49+
end
50+
51+
def test_lost_test_with_heartbeat_monitor
52+
_, err = capture_subprocess_io do
53+
2.times.map do |i|
54+
Thread.start do
55+
system(
56+
{ 'BUILDKITE' => '1' },
57+
@exe, 'run',
58+
'--queue', @redis_url,
59+
'--seed', 'foobar',
60+
'--build', '1',
61+
'--worker', i.to_s,
62+
'--timeout', '1',
63+
'--max-requeues', '1',
64+
'--requeue-tolerance', '1',
65+
'--heartbeat', '1',
66+
'-Itest',
67+
'test/lost_test.rb',
68+
chdir: 'test/fixtures/',
69+
)
70+
end
71+
end.each(&:join)
72+
end
73+
74+
assert_empty err
75+
76+
Tempfile.open('warnings') do |warnings_file|
77+
out, err = capture_subprocess_io do
78+
system(
79+
@exe, 'report',
80+
'--queue', @redis_url,
81+
'--build', '1',
82+
'--timeout', '1',
83+
'--warnings-file', warnings_file.path,
84+
'--heartbeat',
85+
chdir: 'test/fixtures/',
86+
)
87+
end
88+
89+
assert_empty err
90+
result = normalize(out.lines[1].strip)
91+
assert_equal "Ran 1 tests, 0 assertions, 0 failures, 0 errors, 0 skips, 0 requeues in X.XXs (aggregated)", result
92+
warnings = JSON.parse(warnings_file.read)
93+
assert_equal 1, warnings.size
94+
end
95+
end
96+
97+
def test_verbose_reporter
98+
out, err = capture_subprocess_io do
99+
system(
100+
{ 'BUILDKITE' => '1' },
101+
@exe, 'run',
102+
'--queue', @redis_url,
103+
'--seed', 'foobar',
104+
'--build', '1',
105+
'--worker', '1',
106+
'--timeout', '1',
107+
'--max-requeues', '1',
108+
'--requeue-tolerance', '1',
109+
'-Itest',
110+
'test/dummy_test.rb',
111+
'-v',
112+
chdir: 'test/fixtures/',
113+
)
114+
end
115+
116+
assert_empty err
117+
assert_match(/ATest#test_foo \d+\.\d+ = S/, normalize(out)) # verbose test ouptut
118+
result = normalize(out.lines.last.strip)
119+
assert_equal '--- Ran 11 tests, 8 assertions, 2 failures, 1 errors, 1 skips, 4 requeues in X.XXs', result
120+
end
121+
122+
def test_debug_log
123+
Tempfile.open('debug_log') do |log_file|
124+
out, err = capture_subprocess_io do
125+
system(
126+
{ 'BUILDKITE' => '1' },
127+
@exe, 'run',
128+
'--queue', @redis_url,
129+
'--seed', 'foobar',
130+
'--build', '1',
131+
'--worker', '1',
132+
'--timeout', '1',
133+
'--max-requeues', '1',
134+
'--requeue-tolerance', '1',
135+
'-Itest',
136+
'test/dummy_test.rb',
137+
'--debug-log', log_file.path,
138+
chdir: 'test/fixtures/',
139+
)
140+
end
141+
142+
assert_includes File.read(log_file.path), 'INFO -- : Finished \'["exists", "b:c4ca4238:w:1:q"]\': 0'
143+
assert_empty err
144+
result = normalize(out.lines.last.strip)
145+
assert_equal '--- Ran 11 tests, 8 assertions, 2 failures, 1 errors, 1 skips, 4 requeues in X.XXs', result
146+
end
147+
end
148+
25149
def test_buildkite_output
26150
out, err = capture_subprocess_io do
27151
system(
@@ -240,7 +364,7 @@ def test_retry_fails_when_test_run_is_expired
240364
assert_equal 'Ran 100 tests, 100 assertions, 0 failures, 0 errors, 0 skips, 0 requeues in X.XXs', output
241365

242366
one_day = 60 * 60 * 24
243-
key = ['build', "1", "created-at"].join(':')
367+
key = CI::Queue::Redis::KeyShortener.key("1", "created-at")
244368
@redis.set(key, Time.now - one_day)
245369

246370
out, err = capture_subprocess_io do

0 commit comments

Comments
 (0)