diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 83650daa676b5..d47582528c12e 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -16,7 +16,7 @@ * Add instrumentation for Active Record transactions - Allows subscribing to transaction events for tracking/instrumentation. The event payload contains the connection, as well as timing details. + Allows subscribing to transaction events for tracking/instrumentation. The event payload contains the connection and the outcome (commit, rollback, restart, incomplete), as well as timing details. ```ruby ActiveSupport::Notifications.subscribe("transaction.active_record") do |event| diff --git a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb index 648dc38ed7af9..9a8d1f9595276 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb @@ -78,21 +78,24 @@ class TransactionInstrumenter def initialize(payload = {}) @handle = nil @started = false - @payload = payload + @payload = nil + @base_payload = payload end def start return if @started @started = true + @payload = @base_payload.dup @handle = ActiveSupport::Notifications.instrumenter.build_handle("transaction.active_record", @payload) @handle.start end - def finish + def finish(outcome) return unless @started @started = false + @payload[:outcome] = outcome @handle.finish end end @@ -163,7 +166,7 @@ def restartable? end def incomplete! - @instrumenter.finish + @instrumenter.finish(:incomplete) end def materialize! @@ -334,7 +337,7 @@ def materialize! def restart return unless materialized? - @instrumenter.finish + @instrumenter.finish(:restart) @instrumenter.start connection.rollback_to_savepoint(savepoint_name) @@ -345,13 +348,13 @@ def rollback connection.rollback_to_savepoint(savepoint_name) if materialized? end @state.rollback! - @instrumenter.finish + @instrumenter.finish(:rollback) end def commit connection.release_savepoint(savepoint_name) if materialized? @state.commit! - @instrumenter.finish + @instrumenter.finish(:commit) end def full_rollback?; false; end @@ -372,7 +375,7 @@ def materialize! def restart return unless materialized? - @instrumenter.finish + @instrumenter.finish(:restart) if connection.supports_restart_db_transaction? @instrumenter.start @@ -386,13 +389,13 @@ def restart def rollback connection.rollback_db_transaction if materialized? @state.full_rollback! - @instrumenter.finish + @instrumenter.finish(:rollback) end def commit connection.commit_db_transaction if materialized? @state.full_commit! - @instrumenter.finish + @instrumenter.finish(:commit) end end diff --git a/activerecord/test/cases/transaction_instrumentation_test.rb b/activerecord/test/cases/transaction_instrumentation_test.rb index e80811d36ecc3..b27226d649653 100644 --- a/activerecord/test/cases/transaction_instrumentation_test.rb +++ b/activerecord/test/cases/transaction_instrumentation_test.rb @@ -13,6 +13,7 @@ def test_transaction_instrumentation_on_commit notified = false subscriber = ActiveSupport::Notifications.subscribe("transaction.active_record") do |event| assert event.payload[:connection] + assert_equal :commit, event.payload[:outcome] notified = true end @@ -31,6 +32,7 @@ def test_transaction_instrumentation_on_rollback notified = false subscriber = ActiveSupport::Notifications.subscribe("transaction.active_record") do |event| assert event.payload[:connection] + assert_equal :rollback, event.payload[:outcome] notified = true end @@ -60,6 +62,9 @@ def test_transaction_instrumentation_with_savepoints end assert_equal 2, events.count + savepoint, real = events + assert_equal :commit, savepoint.payload[:outcome] + assert_equal :commit, real.payload[:outcome] ensure ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber end @@ -100,6 +105,9 @@ def test_transaction_instrumentation_with_restart_parent_transaction_on_rollback end assert_equal 2, events.count + restart, real = events + assert_equal :restart, restart.payload[:outcome] + assert_equal :rollback, real.payload[:outcome] ensure ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber end @@ -140,6 +148,10 @@ def test_transaction_instrumentation_with_restart_savepoint_parent_transactions end assert_equal 3, events.count + restart, savepoint, real = events + assert_equal :restart, restart.payload[:outcome] + assert_equal :commit, savepoint.payload[:outcome] + assert_equal :commit, real.payload[:outcome] ensure ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber end @@ -239,6 +251,7 @@ def test_transaction_instrumentation_on_failed_rollback notified = false subscriber = ActiveSupport::Notifications.subscribe("transaction.active_record") do |event| + assert_equal :incomplete, event.payload[:outcome] notified = true end