summaryrefslogtreecommitdiff
path: root/lib/gitlab/sherlock/transaction.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/sherlock/transaction.rb')
-rw-r--r--lib/gitlab/sherlock/transaction.rb85
1 files changed, 85 insertions, 0 deletions
diff --git a/lib/gitlab/sherlock/transaction.rb b/lib/gitlab/sherlock/transaction.rb
new file mode 100644
index 00000000000..5cb3e86aa4e
--- /dev/null
+++ b/lib/gitlab/sherlock/transaction.rb
@@ -0,0 +1,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