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
75
76
77
78
79
80
81
82
83
84
85
|
module Gitlab
module Sherlock
class Transaction
attr_reader :id, :type, :path, :queries, :file_samples, :started_at,
:finished_at
def initialize(type, path)
@id = SecureRandom.uuid
@type = type
@path = path
@duration = 0
@queries = []
@file_samples = []
@started_at = nil
@finished_at = nil
@thread = Thread.current
end
def run
@started_at = Time.now
subscriber = subscribe_to_active_record
retval = profile_lines { yield }
@finished_at = Time.now
ActiveSupport::Notifications.unsubscribe(subscriber)
retval
end
def duration
@started_at && @finished_at ? @finished_at - @started_at : 0
end
def to_param
@id
end
def sorted_queries
@queries.sort { |a, b| b.duration <=> a.duration }
end
def sorted_file_samples
@file_samples.sort { |a, b| b.duration <=> a.duration }
end
def find_query(id)
@queries.find { |query| query.id == id }
end
def find_file_sample(id)
@file_samples.find { |sample| sample.id == id }
end
def track_query(query, bindings, start, finish)
@queries << Query.new_with_bindings(query, bindings, start, finish)
end
def profile_lines
retval = nil
if Sherlock.enable_line_profiler?
retval, @file_samples = LineProfiler.new.profile { yield }
else
retval = yield
end
retval
end
def subscribe_to_active_record
ActiveSupport::Notifications.subscribe('sql.active_record') do |_, start, finish, _, data|
# In case somebody uses a multi-threaded server locally (e.g. Puma) we
# _only_ want to track queries that originate from the transaction
# thread.
next unless Thread.current == @thread
track_query(data[:sql].strip, data[:binds], start, finish)
end
end
end
end
end
|