123456789_123456789_123456789_123456789_123456789_

Class: ActiveRecord::ConnectionAdapters::TransactionManager

Do not use. This class is for internal use only.
Relationships & Source Files
Inherits: Object
Defined in: activerecord/lib/active_record/connection_adapters/abstract/transaction.rb

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(connection) ⇒ TransactionManager

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 407

def initialize(connection)
  @stack = []
  @connection = connection
  @has_unmaterialized_transactions = false
  @materializing_transactions = false
  @lazy_transactions_enabled = true
end

Instance Attribute Details

#lazy_transactions_enabled?Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 466

def lazy_transactions_enabled?
  @lazy_transactions_enabled
end

#restorable?Boolean (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 482

def restorable?
  @stack.none?(&:dirty?)
end

Instance Method Details

#after_failure_actions(transaction, error) (private)

Deallocate invalidated prepared statements outside of the transaction

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 596

def after_failure_actions(transaction, error)
  return unless transaction.is_a?(RealTransaction)
  return unless error.is_a?(ActiveRecord::PreparedStatementCacheExpired)
  @connection.clear_cache!
end

#begin_transaction(isolation: nil, joinable: true, _lazy: true)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 415

def begin_transaction(isolation: nil, joinable: true, _lazy: true)
  @connection.lock.synchronize do
    run_commit_callbacks = !current_transaction.joinable?
    transaction =
      if @stack.empty?
        RealTransaction.new(
          @connection,
          isolation: isolation,
          joinable: joinable,
          run_commit_callbacks: run_commit_callbacks
        )
      elsif current_transaction.restartable?
        RestartParentTransaction.new(
          @connection,
          current_transaction,
          isolation: isolation,
          joinable: joinable,
          run_commit_callbacks: run_commit_callbacks
        )
      else
        SavepointTransaction.new(
          @connection,
          "active_record_#{@stack.size}",
          current_transaction,
          isolation: isolation,
          joinable: joinable,
          run_commit_callbacks: run_commit_callbacks
        )
      end

    unless transaction.materialized?
      if @connection.supports_lazy_transactions? && lazy_transactions_enabled? && _lazy
        @has_unmaterialized_transactions = true
      else
        transaction.materialize!
      end
    end
    @stack.push(transaction)
    transaction
  end
end

#commit_transaction

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 502

def commit_transaction
  @connection.lock.synchronize do
    transaction = @stack.last

    begin
      transaction.before_commit_records
    ensure
      @stack.pop
    end

    dirty_current_transaction if transaction.dirty?

    transaction.commit
    transaction.commit_records
  end
end

#current_transaction

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 588

def current_transaction
  @stack.last || NULL_TRANSACTION
end

#dirty_current_transaction

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 470

def dirty_current_transaction
  current_transaction.dirty!
end

#disable_lazy_transactions!

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 457

def disable_lazy_transactions!
  materialize_transactions
  @lazy_transactions_enabled = false
end

#enable_lazy_transactions!

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 462

def enable_lazy_transactions!
  @lazy_transactions_enabled = true
end

#materialize_transactions

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 486

def materialize_transactions
  return if @materializing_transactions

  if @has_unmaterialized_transactions
    @connection.lock.synchronize do
      begin
        @materializing_transactions = true
        @stack.each { |t| t.materialize! unless t.materialized? }
      ensure
        @materializing_transactions = false
      end
      @has_unmaterialized_transactions = false
    end
  end
end

#open_transactions

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 584

def open_transactions
  @stack.size
end

#restore_transactions

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 474

def restore_transactions
  return false unless restorable?

  @stack.each(&:restore!)

  true
end

#rollback_transaction(transaction = nil)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 519

def rollback_transaction(transaction = nil)
  @connection.lock.synchronize do
    transaction ||= @stack.last
    begin
      transaction.rollback
    ensure
      @stack.pop if @stack.last == transaction
    end
    transaction.rollback_records
  end
end

#within_new_transaction(isolation: nil, joinable: true)

[ GitHub ]

  
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 531

def within_new_transaction(isolation: nil, joinable: true)
  @connection.lock.synchronize do
    transaction = begin_transaction(isolation: isolation, joinable: joinable)
    begin
      ret = yield
      completed = true
      ret
    rescue Exception => error
      rollback_transaction
      after_failure_actions(transaction, error)

      raise
    ensure
      unless error
        # In 7.1 we enforce timeout >= 0.4.0 which no longer use throw, so we can
        # go back to the original behavior of committing on non-local return.
        # If users are using throw, we assume it's not an error case.
        completed = true if ActiveRecord.commit_transaction_on_non_local_return

        if Thread.current.status == "aborting"
          rollback_transaction
        elsif !completed && transaction.written
          ActiveRecord.deprecator.warn(<<~EOW)
            A transaction is being rolled back because the transaction block was
            exited using `return`, `break` or `throw`.
            In Rails 7.2 this transaction will be committed instead.
            To opt-in to the new behavior now and suppress this warning
            you can set:

              Rails.application.config.active_record.commit_transaction_on_non_local_return = true
          EOW
          rollback_transaction
        else
          begin
            commit_transaction
          rescue ActiveRecord::ConnectionFailed
            transaction.invalidate! unless transaction.state.completed?
            raise
          rescue Exception
            rollback_transaction(transaction) unless transaction.state.completed?
            raise
          end
        end
      end
    end
  ensure
    unless transaction&.state&.completed?
      @connection.throw_away!
      transaction&.incomplete!
    end
  end
end