Skip to content

Commit

Permalink
Handle request-start header in microseconds and nanoseconds
Browse files Browse the repository at this point in the history
  • Loading branch information
adamlogic committed Mar 14, 2023
1 parent 25632f9 commit fefa177
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 9 deletions.
20 changes: 16 additions & 4 deletions judoscale-ruby/lib/judoscale/request_metrics.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

module Judoscale
class RequestMetrics
MILLISECONDS_CUTOFF = Date.new(2000, 1, 1).to_time.to_i * 1000
MICROSECONDS_CUTOFF = MILLISECONDS_CUTOFF * 1000
NANOSECONDS_CUTOFF = MICROSECONDS_CUTOFF * 1000

attr_reader :request_id, :size, :network_time

def initialize(env, config = Config.instance)
Expand All @@ -20,15 +24,23 @@ def started_at
if @request_start_header
# There are several variants of this header. We handle these:
# - whole milliseconds (Heroku)
# - whole microseconds (???)
# - whole nanoseconds (Render)
# - fractional seconds (NGINX)
# - preceeding "t=" (NGINX)
value = @request_start_header.gsub(/[^0-9.]/, "").to_f

case value
when 0..100_000_000_000 then Time.at(value)
when 100_000_000_000..100_000_000_000_000 then Time.at(value / 1000.0)
else Time.at(value / 1_000_000.0)
# `value` could be seconds, milliseconds, microseconds or nanoseconds.
# We use some arbitrary cutoffs to determine which one it is.

if value > NANOSECONDS_CUTOFF
Time.at(value / 1_000_000_000.0)
elsif value > MICROSECONDS_CUTOFF
Time.at(value / 1_000_000.0)
elsif value > MILLISECONDS_CUTOFF
Time.at(value / 1000.0)
else
Time.at(value)
end
end
end
Expand Down
16 changes: 11 additions & 5 deletions judoscale-ruby/test/request_metrics_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,26 @@ module Judoscale
ended_at = started_at + 1
env["HTTP_X_REQUEST_START"] = "t=#{format "%.3f", started_at.to_f}"

# The queue time might return 999 or 1000 depending to time + float + format conversion,
# even when freezing time.
_(request.queue_time(ended_at)).must_be_within_delta 1000, 1
end
end

it "handles X_REQUEST_START in nanoseconds" do
it "handles X_REQUEST_START in microseconds" do
freeze_time do
started_at = Time.now.utc - 2
ended_at = started_at + 1
env["HTTP_X_REQUEST_START"] = (started_at.to_f * 1_000_000).to_i.to_s

# The queue time might return 999 or 1000 depending to time + float + format conversion,
# even when freezing time.
_(request.queue_time(ended_at)).must_be_within_delta 1000, 1
end
end

it "handles X_REQUEST_START in nanoseconds" do
freeze_time do
started_at = Time.now.utc - 2
ended_at = started_at + 1
env["HTTP_X_REQUEST_START"] = (started_at.to_f * 1_000_000_000).to_i.to_s

_(request.queue_time(ended_at)).must_be_within_delta 1000, 1
end
end
Expand Down

0 comments on commit fefa177

Please sign in to comment.