DEV: Add site setting to allow collection of GC stats during requests (#22898)

What does this change do?

This change adds a hidden `track_gc_stat_per_request` site setting which
when enabled will track the time spent in GC, major GC count and minor
GC count during a request.

Why is this change needed?

We have plans to tune our GC in production but without any
instrumentation, we will not be able to know if our tuning is effective
or not. This commit takes the first step at instrumenting some basic GC
stats in core during a request which can then be consumed by the discourse-prometheus plugin.
This commit is contained in:
Alan Guo Xiang Tan 2023-08-02 09:16:32 +08:00 committed by GitHub
parent e88452a247
commit e8f7b62752
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 2 deletions

View File

@ -2148,6 +2148,9 @@ developer:
max_sidebar_section_links: max_sidebar_section_links:
default: 50 default: 50
hidden: true hidden: true
track_gc_stat_per_request:
default: false
hidden: true
navigation: navigation:
navigation_menu: navigation_menu:

View File

@ -89,8 +89,13 @@ class MethodProfiler
end end
def self.start(transfer = nil) def self.start(transfer = nil)
Thread.current[:_method_profiler] = transfer || Thread.current[:_method_profiler] = if transfer
{ __start: Process.clock_gettime(Process::CLOCK_MONOTONIC) } transfer
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
@ -99,11 +104,22 @@ class MethodProfiler
def self.stop def self.stop
finish = Process.clock_gettime(Process::CLOCK_MONOTONIC) finish = Process.clock_gettime(Process::CLOCK_MONOTONIC)
if data = Thread.current[:_method_profiler] if data = Thread.current[:_method_profiler]
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
end end

View File

@ -37,4 +37,24 @@ 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