124 lines
3 KiB
Ruby
124 lines
3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Gitlab
|
|
module Database
|
|
module Transaction
|
|
class Context
|
|
attr_reader :context
|
|
|
|
LOG_SAVEPOINTS_THRESHOLD = 1 # 1 `SAVEPOINT` created in a transaction
|
|
LOG_DURATION_S_THRESHOLD = 120 # transaction that is running for 2 minutes or longer
|
|
LOG_THROTTLE_DURATION = 1
|
|
|
|
def initialize
|
|
@context = {}
|
|
end
|
|
|
|
def set_start_time
|
|
@context[:start_time] = current_timestamp
|
|
end
|
|
|
|
def set_depth(depth)
|
|
@context[:depth] = [@context[:depth].to_i, depth].max
|
|
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 track_sql(sql)
|
|
(@context[:queries] ||= []).push(sql)
|
|
end
|
|
|
|
def track_backtrace(backtrace)
|
|
cleaned_backtrace = Gitlab::BacktraceCleaner.clean_backtrace(backtrace)
|
|
(@context[:backtraces] ||= []).push(cleaned_backtrace)
|
|
end
|
|
|
|
def duration
|
|
return unless @context[:start_time].present?
|
|
|
|
current_timestamp - @context[:start_time]
|
|
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 should_log?
|
|
return false if logged_already?
|
|
|
|
savepoints_threshold_exceeded? || duration_threshold_exceeded?
|
|
end
|
|
|
|
def commit
|
|
log(:commit)
|
|
end
|
|
|
|
def rollback
|
|
log(:rollback)
|
|
end
|
|
|
|
def backtraces
|
|
@context[:backtraces].to_a
|
|
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,
|
|
savepoint_backtraces: backtraces
|
|
}
|
|
|
|
application_info(attributes)
|
|
end
|
|
|
|
def application_info(attributes)
|
|
Gitlab::AppJsonLogger.info(attributes)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|