mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 05:21:50 +08:00
DEV: Seperate concerns of tracking GC stat from MethodProfiler
(#22921)
Why this change?
This is a follow up to e8f7b62752
.
Tracking of GC stats didn't really belong in the `MethodProfiler` class
so we want to extract that concern into its own class.
As part of this PR, the `track_gc_stat_per_request` site setting has
also been renamed to `instrument_gc_stat_per_request`.
This commit is contained in:
parent
e8f7b62752
commit
773b22e8d0
|
@ -43,6 +43,7 @@ Rails.autoloaders.each do |autoloader|
|
||||||
"json" => "JSON",
|
"json" => "JSON",
|
||||||
"ssrf_detector" => "SSRFDetector",
|
"ssrf_detector" => "SSRFDetector",
|
||||||
"http" => "HTTP",
|
"http" => "HTTP",
|
||||||
|
"gc_stat_instrumenter" => "GCStatInstrumenter",
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
Rails.autoloaders.main.ignore(
|
Rails.autoloaders.main.ignore(
|
||||||
|
|
|
@ -2148,7 +2148,7 @@ developer:
|
||||||
max_sidebar_section_links:
|
max_sidebar_section_links:
|
||||||
default: 50
|
default: 50
|
||||||
hidden: true
|
hidden: true
|
||||||
track_gc_stat_per_request:
|
instrument_gc_stat_per_request:
|
||||||
default: false
|
default: false
|
||||||
hidden: true
|
hidden: true
|
||||||
|
|
||||||
|
|
17
lib/gc_stat_instrumenter.rb
Normal file
17
lib/gc_stat_instrumenter.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class GCStatInstrumenter
|
||||||
|
def self.instrument
|
||||||
|
start_gc_stat = GC.stat
|
||||||
|
yield
|
||||||
|
end_gc_stat = GC.stat
|
||||||
|
|
||||||
|
{
|
||||||
|
gc: {
|
||||||
|
time: (end_gc_stat[:time] - start_gc_stat[:time]) / 1000.0,
|
||||||
|
major_count: end_gc_stat[:major_gc_count] - start_gc_stat[:major_gc_count],
|
||||||
|
minor_count: end_gc_stat[:minor_gc_count] - start_gc_stat[:minor_gc_count],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -89,13 +89,8 @@ class MethodProfiler
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.start(transfer = nil)
|
def self.start(transfer = nil)
|
||||||
Thread.current[:_method_profiler] = if transfer
|
Thread.current[:_method_profiler] = transfer ||
|
||||||
transfer
|
{ __start: Process.clock_gettime(Process::CLOCK_MONOTONIC) }
|
||||||
else
|
|
||||||
data = { __start: Process.clock_gettime(Process::CLOCK_MONOTONIC) }
|
|
||||||
data[:gc_stat] = GC.stat if SiteSetting.track_gc_stat_per_request
|
|
||||||
data
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.clear
|
def self.clear
|
||||||
|
@ -109,15 +104,6 @@ class MethodProfiler
|
||||||
Thread.current[:_method_profiler] = nil
|
Thread.current[:_method_profiler] = nil
|
||||||
start = data.delete(:__start)
|
start = data.delete(:__start)
|
||||||
data[:total_duration] = finish - start
|
data[:total_duration] = finish - start
|
||||||
|
|
||||||
if start_gc_stat = data.delete(:gc_stat)
|
|
||||||
end_gc_stat = GC.stat
|
|
||||||
|
|
||||||
data[:gc] ||= {}
|
|
||||||
data[:gc][:time] = (end_gc_stat[:time] - start_gc_stat[:time]) / 1000.0
|
|
||||||
data[:gc][:major_count] = end_gc_stat[:major_gc_count] - start_gc_stat[:major_gc_count]
|
|
||||||
data[:gc][:minor_count] = end_gc_stat[:minor_gc_count] - start_gc_stat[:minor_gc_count]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
data
|
data
|
||||||
|
|
|
@ -201,6 +201,7 @@ class Middleware::RequestTracker
|
||||||
def call(env)
|
def call(env)
|
||||||
result = nil
|
result = nil
|
||||||
info = nil
|
info = nil
|
||||||
|
gc_stat_timing = nil
|
||||||
|
|
||||||
# doing this as early as possible so we have an
|
# doing this as early as possible so we have an
|
||||||
# accurate counter
|
# accurate counter
|
||||||
|
@ -225,7 +226,13 @@ class Middleware::RequestTracker
|
||||||
env["discourse.request_tracker"] = self
|
env["discourse.request_tracker"] = self
|
||||||
|
|
||||||
MethodProfiler.start
|
MethodProfiler.start
|
||||||
result = @app.call(env)
|
|
||||||
|
if SiteSetting.instrument_gc_stat_per_request
|
||||||
|
gc_stat_timing = GCStatInstrumenter.instrument { result = @app.call(env) }
|
||||||
|
else
|
||||||
|
result = @app.call(env)
|
||||||
|
end
|
||||||
|
|
||||||
info = MethodProfiler.stop
|
info = MethodProfiler.stop
|
||||||
|
|
||||||
# possibly transferred?
|
# possibly transferred?
|
||||||
|
@ -263,7 +270,11 @@ class Middleware::RequestTracker
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
log_request_info(env, result, info, request) if !env["discourse.request_tracker.skip"]
|
|
||||||
|
if !env["discourse.request_tracker.skip"]
|
||||||
|
info.merge!(gc_stat_timing) if gc_stat_timing
|
||||||
|
log_request_info(env, result, info, request)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def log_later(data)
|
def log_later(data)
|
||||||
|
|
18
spec/lib/gc_stat_instrumenter_spec.rb
Normal file
18
spec/lib/gc_stat_instrumenter_spec.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
RSpec.describe GCStatInstrumenter do
|
||||||
|
describe ".instrument" do
|
||||||
|
it "returns a hash with the GC time, major and minor GC count for executing the given block" do
|
||||||
|
result =
|
||||||
|
described_class.instrument do
|
||||||
|
GC.start(full_mark: true) # Major GC
|
||||||
|
GC.start(full_mark: false) # Minor GC
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(result[:gc]).to be_present
|
||||||
|
expect(result[:gc][:time]).to be >= 0.0
|
||||||
|
expect(result[:gc][:major_count]).to eq(1)
|
||||||
|
expect(result[:gc][:minor_count]).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -37,24 +37,4 @@ RSpec.describe MethodProfiler do
|
||||||
|
|
||||||
expect(result[:at_beach][:calls]).to eq(2)
|
expect(result[:at_beach][:calls]).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "profiles GC stat information when `track_gc_stat_per_request` site setting has been enabled" do
|
|
||||||
MethodProfiler.start
|
|
||||||
GC.start(full_mark: false) # Minor GC
|
|
||||||
result = MethodProfiler.stop
|
|
||||||
|
|
||||||
expect(result[:gc]).not_to be_present
|
|
||||||
|
|
||||||
SiteSetting.track_gc_stat_per_request = true
|
|
||||||
|
|
||||||
MethodProfiler.start
|
|
||||||
GC.start(full_mark: true) # Major GC
|
|
||||||
GC.start(full_mark: false) # Minor GC
|
|
||||||
result = MethodProfiler.stop
|
|
||||||
|
|
||||||
expect(result[:gc]).to be_present
|
|
||||||
expect(result[:gc][:time]).to be >= 0.0
|
|
||||||
expect(result[:gc][:major_count]).to eq(1)
|
|
||||||
expect(result[:gc][:minor_count]).to eq(1)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -661,6 +661,7 @@ RSpec.describe Middleware::RequestTracker do
|
||||||
lambda do |env|
|
lambda do |env|
|
||||||
sql_calls.times { User.where(id: -100).pluck(:id) }
|
sql_calls.times { User.where(id: -100).pluck(:id) }
|
||||||
redis_calls.times { Discourse.redis.get("x") }
|
redis_calls.times { Discourse.redis.get("x") }
|
||||||
|
yield if block_given?
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -749,6 +750,36 @@ RSpec.describe Middleware::RequestTracker do
|
||||||
expect(headers["X-Runtime"].to_f).to be > 0
|
expect(headers["X-Runtime"].to_f).to be > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "correctly logs GC stats when `instrument_gc_stat_per_request` site setting has been enabled" do
|
||||||
|
tracker =
|
||||||
|
Middleware::RequestTracker.new(
|
||||||
|
app([200, {}, []]) do
|
||||||
|
GC.start(full_mark: true) # Major GC
|
||||||
|
GC.start(full_mark: false) # Minor GC
|
||||||
|
end,
|
||||||
|
)
|
||||||
|
|
||||||
|
tracker.call(env)
|
||||||
|
|
||||||
|
expect(@data[:timing][:gc]).to eq(nil)
|
||||||
|
|
||||||
|
SiteSetting.instrument_gc_stat_per_request = true
|
||||||
|
|
||||||
|
tracker =
|
||||||
|
Middleware::RequestTracker.new(
|
||||||
|
app([200, {}, []]) do
|
||||||
|
GC.start(full_mark: true) # Major GC
|
||||||
|
GC.start(full_mark: false) # Minor GC
|
||||||
|
end,
|
||||||
|
)
|
||||||
|
|
||||||
|
tracker.call(env)
|
||||||
|
|
||||||
|
expect(@data[:timing][:gc][:time]).to be > 0.0
|
||||||
|
expect(@data[:timing][:gc][:major_count]).to eq(1)
|
||||||
|
expect(@data[:timing][:gc][:minor_count]).to eq(1)
|
||||||
|
end
|
||||||
|
|
||||||
it "can correctly log messagebus request types" do
|
it "can correctly log messagebus request types" do
|
||||||
tracker = Middleware::RequestTracker.new(app([200, {}, []]))
|
tracker = Middleware::RequestTracker.new(app([200, {}, []]))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user