blob: 6cf82e0e3cd940ee111690c37f7df2585f76417c (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
# frozen_string_literal: true
module Gitlab
module Database
module QueryAnalyzers
# The purpose of this analyzer is to log query activity that contains `IN` clauses having more that 2500 items
# as this type of query can cause performance degradation in the database.
#
# The feature flag should prevent sampling going above 1% or 0.01% of queries hitting
# to avoid performance issues
class LogLargeInLists < Base
REGEX = /\bIN\s*\(([$?\d\s*,]*)\)+/i
MIN_QUERY_SIZE = 10_000
IN_SIZE_LIMIT = 2_500
EVENT_NAMES = %w[load pluck].freeze
EXCLUDE_FROM_TRACE = %w[
lib/gitlab/database/query_analyzer.rb
lib/gitlab/database/query_analyzers/log_large_in_lists.rb
].freeze
class << self
def enabled?
::Feature::FlipperFeature.table_exists? &&
Feature.enabled?(:log_large_in_list_queries, type: :ops)
end
# Skips queries containing less than 10000 chars or any other events than +load+ and +pluck+
def requires_tracking?(parsed)
return false if parsed.sql.size < MIN_QUERY_SIZE
EVENT_NAMES.include?(parsed.event_name)
end
def analyze(parsed)
result = check_argument_size(parsed.sql)
log(result, parsed.event_name) if result.any?
end
private
def check_argument_size(sql)
matches = sql.scan(REGEX).flatten
return [] if matches.empty?
matches.filter_map do |match|
match_size = match.split(',').size
match_size if match_size > IN_SIZE_LIMIT
end
end
def log(result, event_name)
Gitlab::AppLogger.warn(
message: 'large_in_list_found',
matches: result.size,
event_name: event_name,
in_list_size: result.join(', '),
stacktrace: backtrace.first(5)
)
end
def backtrace
Gitlab::BacktraceCleaner.clean_backtrace(caller).reject do |line|
EXCLUDE_FROM_TRACE.any? { |exclusion| line.include?(exclusion) }
end
end
end
end
end
end
end
|