Skip to content

Commit 0e18e32

Browse files
nateberkopecclaude
andcommitted
Refactor lib/ for idiomatic, terse Ruby
- metric.rb: Inline single-use constants into the Yabeda.configure block - middleware.rb: Extract measure_queue_time to shrink call to 2 lines, collapse two indirection methods (request_start_timestamp, parse_header_timestamp) into one, use [x, 0].max clamping pattern, endless methods for single-expression inner classes, remove unused parser: keyword arg, inline header key strings - header_timestamp_parser.rb: Inline trivial one-liner helpers (first_header_value, extract_numeric_token), use find + between? for normalize, shorten constant names, implicit nil returns Line counts: middleware 52 (was 85), parser 31 (was 51), metric 12 (was 27). All methods ≤5 lines, all classes well under 100, max 4 params. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8ca4a19 commit 0e18e32

File tree

3 files changed

+34
-100
lines changed

3 files changed

+34
-100
lines changed

lib/yabeda/rack/queue/header_timestamp_parser.rb

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,27 @@ module Yabeda
44
module Rack
55
module Queue
66
class HeaderTimestampParser
7-
MIN_EPOCH_SECONDS = Time.utc(2000, 1, 1).to_f
8-
FUTURE_TOLERANCE_SECONDS = 30.0
9-
NORMALIZATION_DIVISORS = [1_000_000.0, 1_000.0, 1.0].freeze
10-
NUMBER_PATTERN = /[+-]?(?:\d+(?:\.\d+)?|\.\d+)/
11-
T_EQUALS_PATTERN = /t\s*=\s*(#{NUMBER_PATTERN.source})/i
7+
MIN_EPOCH = Time.utc(2000, 1, 1).to_f
8+
FUTURE_TOLERANCE = 30.0
9+
DIVISORS = [1_000_000.0, 1_000.0, 1.0].freeze
10+
NUMBER_RE = /[+-]?(?:\d+(?:\.\d+)?|\.\d+)/
11+
T_EQUALS_RE = /t\s*=\s*(#{NUMBER_RE.source})/i
1212

1313
def parse(value, now:)
14-
first_value = first_header_value(value)
15-
return nil if first_value.empty?
14+
first = value.to_s.split(",", 2).first.to_s.strip
15+
return if first.empty?
1616

17-
token = extract_numeric_token(first_value)
18-
return nil if token.nil?
19-
20-
normalize(Float(token), now)
17+
token = first[T_EQUALS_RE, 1] || first[NUMBER_RE, 0]
18+
normalize(Float(token), now) if token
2119
rescue ArgumentError, TypeError
22-
nil
2320
end
2421

2522
private
2623

27-
def first_header_value(value)
28-
value.to_s.split(",", 2).first.to_s.strip
29-
end
30-
31-
def extract_numeric_token(value)
32-
value[T_EQUALS_PATTERN, 1] || value[NUMBER_PATTERN, 0]
33-
end
34-
35-
def normalize(raw_timestamp, now)
36-
max_allowed = now + FUTURE_TOLERANCE_SECONDS
37-
38-
NORMALIZATION_DIVISORS.each do |divisor|
39-
candidate = raw_timestamp / divisor
40-
next if candidate < MIN_EPOCH_SECONDS
41-
next if candidate > max_allowed
42-
43-
return candidate
44-
end
45-
46-
nil
24+
def normalize(raw, now)
25+
max = now + FUTURE_TOLERANCE
26+
divisor = DIVISORS.find { |d| (raw / d).between?(MIN_EPOCH, max) }
27+
raw / divisor if divisor
4728
end
4829
end
4930
end

lib/yabeda/rack/queue/metric.rb

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,11 @@
22

33
require "yabeda"
44

5-
module Yabeda
6-
module Rack
7-
module Queue
8-
HISTOGRAM_BUCKETS = [
9-
0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60
10-
].freeze
11-
12-
METRIC_NAME = :rack_queue_duration
13-
METRIC_GROUP = :rack_queue
14-
METRIC_UNIT = :seconds
15-
METRIC_DESCRIPTION = "Time a request waited in the upstream queue before reaching the application"
16-
end
17-
end
18-
end
19-
205
Yabeda.configure do
21-
group Yabeda::Rack::Queue::METRIC_GROUP do
22-
histogram Yabeda::Rack::Queue::METRIC_NAME,
23-
comment: Yabeda::Rack::Queue::METRIC_DESCRIPTION,
24-
unit: Yabeda::Rack::Queue::METRIC_UNIT,
25-
buckets: Yabeda::Rack::Queue::HISTOGRAM_BUCKETS
6+
group :rack_queue do
7+
histogram :rack_queue_duration,
8+
comment: "Time a request waited in the upstream queue before reaching the application",
9+
unit: :seconds,
10+
buckets: [0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 30, 60]
2611
end
2712
end

lib/yabeda/rack/queue/middleware.rb

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,80 +4,48 @@ module Yabeda
44
module Rack
55
module Queue
66
class Middleware
7-
HEADER_KEYS = %w[HTTP_X_REQUEST_START HTTP_X_QUEUE_START].freeze
8-
REQUEST_BODY_WAIT_KEY = "puma.request_body_wait"
9-
107
class StderrLogger
11-
def warn(message)
12-
Kernel.warn(message)
13-
end
8+
def warn(message) = Kernel.warn(message)
149
end
1510

1611
class YabedaReporter
17-
def observe(value)
18-
Yabeda.rack_queue.rack_queue_duration.measure({}, value)
19-
end
12+
def observe(value) = Yabeda.rack_queue.rack_queue_duration.measure({}, value)
2013
end
2114

22-
def initialize(app, reporter: YabedaReporter.new, parser: HeaderTimestampParser.new, logger: nil, clock: nil)
15+
def initialize(app, reporter: YabedaReporter.new, logger: nil, clock: nil)
2316
@app = app
2417
@reporter = reporter
25-
@parser = parser
18+
@parser = HeaderTimestampParser.new
2619
@logger = logger || StderrLogger.new
2720
@clock = clock || -> { Process.clock_gettime(Process::CLOCK_REALTIME) }
2821
end
2922

3023
def call(env)
31-
x_request_start = env[HEADER_KEYS[0]]
32-
x_queue_start = env[HEADER_KEYS[1]]
33-
34-
if x_request_start || x_queue_start
35-
now = @clock.call
36-
request_start = request_start_timestamp(x_request_start, x_queue_start, now)
37-
report_queue_time(env, now, request_start) if request_start
38-
end
39-
24+
measure_queue_time(env) if env["HTTP_X_REQUEST_START"] || env["HTTP_X_QUEUE_START"]
4025
@app.call(env)
4126
end
4227

4328
private
4429

45-
def request_start_timestamp(x_request_start, x_queue_start, now)
46-
parsed = parse_header_timestamp(x_request_start, now)
47-
return parsed if parsed
48-
49-
parse_header_timestamp(x_queue_start, now)
50-
end
51-
52-
def parse_header_timestamp(value, now)
53-
return nil if value.nil?
54-
55-
@parser.parse(value, now: now)
30+
def measure_queue_time(env)
31+
now = @clock.call
32+
start = @parser.parse(env["HTTP_X_REQUEST_START"], now: now) ||
33+
@parser.parse(env["HTTP_X_QUEUE_START"], now: now)
34+
report_queue_time(env, now, start) if start
5635
end
5736

5837
def report_queue_time(env, now, request_start)
5938
queue_time = now - request_start
60-
if queue_time.negative?
61-
@logger.warn("Negative rack queue duration (#{queue_time}) observed; dropping measurement")
62-
return
63-
end
64-
65-
body_wait = parse_request_body_wait(env[REQUEST_BODY_WAIT_KEY])
66-
queue_time -= body_wait if body_wait
67-
queue_time = 0.0 if queue_time.negative?
39+
return @logger.warn("Negative rack queue duration (#{queue_time}); dropping") if queue_time.negative?
6840

69-
@reporter.observe(queue_time)
41+
body_wait = parse_body_wait(env["puma.request_body_wait"])
42+
@reporter.observe([queue_time - (body_wait || 0), 0.0].max)
7043
end
7144

72-
def parse_request_body_wait(value)
73-
return nil if value.nil?
74-
75-
milliseconds = Float(value)
76-
return nil if milliseconds.negative?
77-
78-
milliseconds / 1_000.0
45+
def parse_body_wait(value)
46+
ms = Float(value)
47+
ms / 1_000.0 unless ms.negative?
7948
rescue ArgumentError, TypeError
80-
nil
8149
end
8250
end
8351
end

0 commit comments

Comments
 (0)