diff options
Diffstat (limited to 'lib/gitlab/database/transaction/context.rb')
-rw-r--r-- | lib/gitlab/database/transaction/context.rb | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/lib/gitlab/database/transaction/context.rb b/lib/gitlab/database/transaction/context.rb new file mode 100644 index 00000000000..a50dd30b75b --- /dev/null +++ b/lib/gitlab/database/transaction/context.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +module Gitlab + module Database + module Transaction + class Context + attr_reader :context + + LOG_DEPTH_THRESHOLD = 8 + LOG_SAVEPOINTS_THRESHOLD = 32 + LOG_DURATION_S_THRESHOLD = 300 + LOG_THROTTLE_DURATION = 1 + + def initialize + @context = {} + end + + def set_start_time + @context[:start_time] = current_timestamp + end + + def increment_savepoints + @context[:savepoints] = @context[:savepoints].to_i + 1 + end + + def increment_rollbacks + @context[:rollbacks] = @context[:rollbacks].to_i + 1 + end + + def increment_releases + @context[:releases] = @context[:releases].to_i + 1 + end + + def set_depth(depth) + @context[:depth] = [@context[:depth].to_i, depth].max + end + + def track_sql(sql) + (@context[:queries] ||= []).push(sql) + end + + def duration + return unless @context[:start_time].present? + + current_timestamp - @context[:start_time] + end + + def depth_threshold_exceeded? + @context[:depth].to_i > LOG_DEPTH_THRESHOLD + end + + def savepoints_threshold_exceeded? + @context[:savepoints].to_i > LOG_SAVEPOINTS_THRESHOLD + end + + def duration_threshold_exceeded? + duration.to_i > LOG_DURATION_S_THRESHOLD + end + + def log_savepoints? + depth_threshold_exceeded? || savepoints_threshold_exceeded? + end + + def log_duration? + duration_threshold_exceeded? + end + + def should_log? + !logged_already? && (log_savepoints? || log_duration?) + end + + def commit + log(:commit) + end + + def rollback + log(:rollback) + end + + private + + def queries + @context[:queries].to_a.join("\n") + end + + def current_timestamp + ::Gitlab::Metrics::System.monotonic_time + end + + def logged_already? + return false if @context[:last_log_timestamp].nil? + + (current_timestamp - @context[:last_log_timestamp].to_i) < LOG_THROTTLE_DURATION + end + + def set_last_log_timestamp + @context[:last_log_timestamp] = current_timestamp + end + + def log(operation) + return unless should_log? + + set_last_log_timestamp + + attributes = { + class: self.class.name, + result: operation, + duration_s: duration, + depth: @context[:depth].to_i, + savepoints_count: @context[:savepoints].to_i, + rollbacks_count: @context[:rollbacks].to_i, + releases_count: @context[:releases].to_i, + sql: queries + } + + application_info(attributes) + end + + def application_info(attributes) + Gitlab::AppJsonLogger.info(attributes) + end + end + end + end +end |