diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-20 17:22:11 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-12-20 17:22:11 +0300 |
commit | 0c872e02b2c822e3397515ec324051ff540f0cd5 (patch) | |
tree | ce2fb6ce7030e4dad0f4118d21ab6453e5938cdd /lib/gitlab/memory/reporter.rb | |
parent | f7e05a6853b12f02911494c4b3fe53d9540d74fc (diff) |
Add latest changes from gitlab-org/gitlab@15-7-stable-eev15.7.0-rc42
Diffstat (limited to 'lib/gitlab/memory/reporter.rb')
-rw-r--r-- | lib/gitlab/memory/reporter.rb | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/lib/gitlab/memory/reporter.rb b/lib/gitlab/memory/reporter.rb new file mode 100644 index 00000000000..710c89c6216 --- /dev/null +++ b/lib/gitlab/memory/reporter.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +module Gitlab + module Memory + class Reporter + attr_reader :reports_path + + def initialize(reports_path: nil, logger: Gitlab::AppLogger) + @reports_path = reports_path || ENV["GITLAB_DIAGNOSTIC_REPORTS_PATH"] || Dir.mktmpdir + @logger = logger + + @worker_id = ::Prometheus::PidProvider.worker_id + @worker_uuid = SecureRandom.uuid + + init_prometheus_metrics + end + + def run_report(report) + return false unless report.active? + + @logger.info( + log_labels( + message: 'started', + perf_report: report.name + )) + + start_monotonic_time = Gitlab::Metrics::System.monotonic_time + start_thread_cpu_time = Gitlab::Metrics::System.thread_cpu_time + + report_file = store_report(report) + + cpu_s = Gitlab::Metrics::System.thread_cpu_duration(start_thread_cpu_time) + duration_s = Gitlab::Metrics::System.monotonic_time - start_monotonic_time + + @logger.info( + log_labels( + message: 'finished', + perf_report: report.name, + cpu_s: cpu_s.round(2), + duration_s: duration_s.round(2), + perf_report_file: report_file, + perf_report_size_bytes: file_size(report_file) + )) + + @report_duration_counter.increment({ report: report.name }, duration_s) + + true + rescue StandardError => e + @logger.error( + log_labels( + message: 'failed', + perf_report: report.name, + error: e.inspect + )) + + false + end + + private + + def store_report(report) + # Store report in tmp subdir while it is still streaming. + # This will clearly separate finished reports from the files we are still writing to. + tmp_dir = File.join(@reports_path, 'tmp') + FileUtils.mkdir_p(tmp_dir) + + report_file = file_name(report) + tmp_file_path = File.join(tmp_dir, report_file) + + io_r, io_w = IO.pipe + pid = nil + File.open(tmp_file_path, 'wb') do |file| + extras = { + in: io_r, + out: file, + err: $stderr + } + pid = Process.spawn('gzip', '--fast', **extras) + io_r.close + + report.run(io_w) + io_w.close + + Process.waitpid(pid) + end + + File.join(@reports_path, report_file).tap do |report_file_path| + FileUtils.mv(tmp_file_path, report_file_path) + end + ensure + [io_r, io_w].each(&:close) + + # Make sure we don't leave any running processes behind. + Gitlab::ProcessManagement.signal(pid, :KILL) if pid + end + + def log_labels(**extra_labels) + { + pid: $$, + worker_id: @worker_id, + perf_report_worker_uuid: @worker_uuid + }.merge(extra_labels) + end + + def file_name(report) + timestamp = Time.current.strftime('%Y-%m-%d.%H:%M:%S:%L') + + report_id = [@worker_id, @worker_uuid].join(".") + + [report.name, timestamp, report_id, 'gz'].compact_blank.join('.') + end + + def file_size(file_path) + File.size(file_path.to_s) + rescue Errno::ENOENT + 0 + end + + def init_prometheus_metrics + default_labels = { pid: @worker_id } + + @report_duration_counter = Gitlab::Metrics.counter( + :gitlab_diag_report_duration_seconds_total, + 'Total time elapsed for running diagnostic report', + default_labels + ) + end + end + end +end |