From c24c885209ac2334dc6f798c394a821ee270bec6 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 28 Aug 2012 11:59:45 -0700 Subject: create a transaction object and point AR objects at that object during a transaction. --- .../abstract/database_statements.rb | 8 +++-- .../connection_adapters/abstract_adapter.rb | 23 ++++++++++++-- activerecord/lib/active_record/core.rb | 1 + activerecord/lib/active_record/transactions.rb | 35 ++++++++++++++++++---- 4 files changed, 55 insertions(+), 12 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 4e1f0e1d62..11e4d34de2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -193,7 +193,8 @@ module ActiveRecord rescue Exception => database_transaction_rollback if transaction_open && !outside_transaction? transaction_open = false - decrement_open_transactions + txn = decrement_open_transactions + txn.aborted! if open_transactions == 0 rollback_db_transaction rollback_transaction_records(true) @@ -208,9 +209,10 @@ module ActiveRecord @transaction_joinable = last_transaction_joinable if outside_transaction? - @open_transactions = 0 + @current_transaction = nil elsif transaction_open - decrement_open_transactions + txn = decrement_open_transactions + txn.committed! begin if open_transactions == 0 commit_db_transaction diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index b3f9187429..27700e4fd2 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -69,6 +69,7 @@ module ActiveRecord @last_use = false @logger = logger @open_transactions = 0 + @current_transaction = nil @pool = pool @query_cache = Hash.new { |h,sql| h[sql] = {} } @query_cache_enabled = false @@ -236,14 +237,30 @@ module ActiveRecord @connection end - attr_reader :open_transactions + def open_transactions + count = 0 + txn = current_transaction + + while txn + count += 1 + txn = txn.next + end + + count + end + + attr_reader :current_transaction def increment_open_transactions - @open_transactions += 1 + @current_transaction = Transaction.new(current_transaction) end def decrement_open_transactions - @open_transactions -= 1 + return unless current_transaction + + txn = current_transaction + @current_transaction = txn.next + txn end def transaction_joinable=(joinable) diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index aad21b8e37..791ff4eaec 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -393,6 +393,7 @@ module ActiveRecord @marked_for_destruction = false @new_record = true @mass_assignment_options = nil + @txn = nil @_start_transaction_state = {} end end diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index 09318879d5..e008b32170 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -1,6 +1,32 @@ require 'thread' module ActiveRecord + class Transaction + attr_reader :next + + def initialize(txn = nil) + @next = txn + @committed = false + @aborted = false + end + + def committed! + @committed = true + end + + def aborted! + @aborted = true + end + + def committed? + @committed + end + + def aborted? + @aborted + end + end + # See ActiveRecord::Transactions::ClassMethods for documentation. module Transactions extend ActiveSupport::Concern @@ -307,11 +333,11 @@ module ActiveRecord def with_transaction_returning_status status = nil self.class.transaction do + @txn = self.class.connection.current_transaction add_to_transaction begin status = yield rescue ActiveRecord::Rollback - @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 status = nil end @@ -327,20 +353,17 @@ module ActiveRecord @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key) @_start_transaction_state[:new_record] = @new_record @_start_transaction_state[:destroyed] = @destroyed - @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1 end # Clear the new record state and id of a record. def clear_transaction_record_state #:nodoc: - @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 - @_start_transaction_state.clear if @_start_transaction_state[:level] < 1 + @_start_transaction_state.clear if @txn.committed? end # Restore the new record state and id of a record that was previously saved by a call to save_record_state. def restore_transaction_record_state(force = false) #:nodoc: unless @_start_transaction_state.empty? - @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1 - if @_start_transaction_state[:level] < 1 || force + if @txn.aborted? || force restore_state = @_start_transaction_state was_frozen = @attributes.frozen? @attributes = @attributes.dup if was_frozen -- cgit v1.2.3