Commit 174f31b2 authored by Quang-Minh Nguyen's avatar Quang-Minh Nguyen

Publish active_record transaction event

Issue https://gitlab.com/gitlab-org/gitlab/-/issues/323163
parent b29b1359
......@@ -313,28 +313,18 @@ module Gitlab
ActiveRecord::Base.prepend(ActiveRecordBaseTransactionMetrics)
end
# observe_transaction_duration is called from ActiveRecordBaseTransactionMetrics.transaction and used to
# record transaction durations.
def self.observe_transaction_duration(duration_seconds)
if current_transaction = ::Gitlab::Metrics::Transaction.current
current_transaction.observe(:gitlab_database_transaction_seconds, duration_seconds) do
docstring "Time spent in database transactions, in seconds"
end
end
rescue Prometheus::Client::LabelSetValidator::LabelSetError => err
# Ensure that errors in recording these metrics don't affect the operation of the application
Gitlab::AppLogger.error("Unable to observe database transaction duration: #{err}")
end
# MonkeyPatch for ActiveRecord::Base for adding observability
module ActiveRecordBaseTransactionMetrics
# A monkeypatch over ActiveRecord::Base.transaction.
# It provides observability into transactional methods.
def transaction(options = {}, &block)
start_time = Gitlab::Metrics::System.monotonic_time
super(options, &block)
ensure
Gitlab::Database.observe_transaction_duration(Gitlab::Metrics::System.monotonic_time - start_time)
extend ActiveSupport::Concern
class_methods do
# A monkeypatch over ActiveRecord::Base.transaction.
# It provides observability into transactional methods.
def transaction(**options, &block)
ActiveSupport::Notifications.instrument('transaction.active_record', { connection: connection }) do
super(**options, &block)
end
end
end
end
end
......
......@@ -441,4 +441,112 @@ RSpec.describe Gitlab::Database do
end
end
end
describe 'ActiveRecordBaseTransactionMetrics' do
def subscribe_events
events = []
begin
subscriber = ActiveSupport::Notifications.subscribe('transaction.active_record') do |e|
events << e
end
yield
ensure
ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber
end
events
end
context 'without a tranastion block' do
it 'does not publish a transaction event' do
events = subscribe_events do
User.first
end
expect(events).to be_empty
end
end
context 'within a transaction block' do
it 'publishes a transaction event' do
events = subscribe_events do
ActiveRecord::Base.transaction do
User.first
end
end
expect(events.length).to be(1)
event = events.first
expect(event).not_to be_nil
expect(event.duration).to be > 0.0
expect(event.payload).to a_hash_including(
connection: be_a(ActiveRecord::ConnectionAdapters::AbstractAdapter)
)
end
end
context 'within an empty transaction block' do
it 'publishes a transaction event' do
events = subscribe_events do
ActiveRecord::Base.transaction {}
end
expect(events.length).to be(1)
event = events.first
expect(event).not_to be_nil
expect(event.duration).to be > 0.0
expect(event.payload).to a_hash_including(
connection: be_a(ActiveRecord::ConnectionAdapters::AbstractAdapter)
)
end
end
context 'within a nested transaction block' do
it 'publishes multiple transaction events' do
events = subscribe_events do
ActiveRecord::Base.transaction do
ActiveRecord::Base.transaction do
ActiveRecord::Base.transaction do
User.first
end
end
end
end
expect(events.length).to be(3)
events.each do |event|
expect(event).not_to be_nil
expect(event.duration).to be > 0.0
expect(event.payload).to a_hash_including(
connection: be_a(ActiveRecord::ConnectionAdapters::AbstractAdapter)
)
end
end
end
context 'within a cancelled transaction block' do
it 'publishes multiple transaction events' do
events = subscribe_events do
ActiveRecord::Base.transaction do
User.first
raise ActiveRecord::Rollback
end
end
expect(events.length).to be(1)
event = events.first
expect(event).not_to be_nil
expect(event.duration).to be > 0.0
expect(event.payload).to a_hash_including(
connection: be_a(ActiveRecord::ConnectionAdapters::AbstractAdapter)
)
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment