123456789_123456789_123456789_123456789_123456789_

Class: ActiveRecord::ConnectionAdapters::Transaction

Do not use. This class is for internal use only.
Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Subclasses:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Instance Chain:
Inherits: ActiveRecord::Transaction
Defined in: activerecord/lib/active_record/connection_adapters/abstract/transaction.rb

Class Method Summary

Instance Attribute Summary

Instance Method Summary

::ActiveRecord::Transaction - Inherited

#after_commit

Registers a block to be called after the current transaction is fully committed.

#after_rollback

Registers a block to be called after the current transaction is rolled back.

#before_commit

Registers a block to be called before the current transaction is fully committed.

#append_callbacks

Constructor Details

.new(connection, isolation: nil, joinable: true, run_commit_callbacks: false) ⇒ Transaction

[ GitHub ]

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

def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
  super()
  @connection = connection
  @state = TransactionState.new
  @records = nil
  @isolation_level = isolation
  @materialized = false
  @joinable = joinable
  @run_commit_callbacks = run_commit_callbacks
  @lazy_enrollment_records = nil
  @dirty = false
  @instrumenter = TransactionInstrumenter.new(connection: connection)
end

Instance Attribute Details

#closed?Boolean (readonly)

[ GitHub ]

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

def closed?; false; end

#connection (readonly)

[ GitHub ]

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

attr_reader :connection, :state, :savepoint_name, :isolation_level

#dirty?Boolean (readonly)

[ GitHub ]

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

def dirty?
  @dirty
end

#full_rollback?Boolean (readonly)

[ GitHub ]

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

def full_rollback?; true; end

#isolation_level (readonly)

[ GitHub ]

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

attr_reader :connection, :state, :savepoint_name, :isolation_level

#joinable?Boolean (readonly)

[ GitHub ]

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

def joinable?; @joinable; end

#materialized?Boolean (readonly)

[ GitHub ]

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

def materialized?
  @materialized
end

#open?Boolean (readonly)

[ GitHub ]

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

def open?; !closed?; end

#restartable?Boolean (readonly)

Can this transaction’s current state be recreated by rollback+begin ?

[ GitHub ]

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

def restartable?
  joinable? && !dirty?
end

#savepoint_name (readonly)

[ GitHub ]

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

attr_reader :connection, :state, :savepoint_name, :isolation_level

#state (readonly)

[ GitHub ]

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

attr_reader :connection, :state, :savepoint_name, :isolation_level

#written (rw)

[ GitHub ]

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

attr_accessor :written

Instance Method Details

#add_record(record, ensure_finalize = true)

[ GitHub ]

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

def add_record(record, ensure_finalize = true)
  @records ||= []
  if ensure_finalize
    @records << record
  else
    @lazy_enrollment_records ||= ObjectSpace::WeakMap.new
    @lazy_enrollment_records[record] = record
  end
end

#before_commit_records

[ GitHub ]

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

def before_commit_records
  if @run_commit_callbacks
    if records
      if ActiveRecord.before_committed_on_all_records
        ite = unique_records

        instances_to_run_callbacks_on = records.each_with_object({}) do |record, candidates|
          candidates[record] = record
        end

        run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
          record.before_committed! if should_run_callbacks
        end
      else
        records.uniq.each(&:before_committed!)
      end
    end

    @callbacks&.each(&:before_commit)
  end
  # Note: When @run_commit_callbacks is false #commit_records takes care of appending
  # remaining callbacks to the parent transaction
end

#commit_records

[ GitHub ]

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

def commit_records
  if records
    begin
      ite = unique_records

      if @run_commit_callbacks
        instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)

        run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
          record.committed!(should_run_callbacks: should_run_callbacks)
        end
      else
        while record = ite.shift
          # if not running callbacks, only adds the record to the parent transaction
          connection.add_transaction_record(record)
        end
      end
    ensure
      ite&.each { |i| i.committed!(should_run_callbacks: false) }
    end
  end

  if @run_commit_callbacks
    @callbacks&.each(&:after_commit)
  elsif @callbacks
    connection.current_transaction.append_callbacks(@callbacks)
  end
end

#dirty!

[ GitHub ]

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

def dirty!
  @dirty = true
end

#incomplete!

[ GitHub ]

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

def incomplete!
  @instrumenter.finish(:incomplete) if materialized?
end

#invalidate!

[ GitHub ]

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

delegate :invalidate!, :invalidated?, to: :@state

#invalidated?Boolean

[ GitHub ]

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

delegate :invalidate!, :invalidated?, to: :@state

#materialize!

[ GitHub ]

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

def materialize!
  @materialized = true
  @instrumenter.start
end

#prepare_instances_to_run_callbacks_on(records) (private)

[ GitHub ]

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

def prepare_instances_to_run_callbacks_on(records)
  records.each_with_object({}) do |record, candidates|
    next unless record.trigger_transactional_callbacks?

    earlier_saved_candidate = candidates[record]

    next if earlier_saved_candidate && record.class.run_commit_callbacks_on_first_saved_instances_in_transaction

    # If the candidate instance destroyed itself in the database, then
    # instances which were added to the transaction afterwards, and which
    # think they updated themselves, are wrong. They should not replace
    # our candidate as an instance to run callbacks on
    next if earlier_saved_candidate&.destroyed? && !record.destroyed?

    # If the candidate instance was created inside of this transaction,
    # then instances which were subsequently loaded from the database
    # and updated need that state transferred to them so that
    # the after_create_commit callbacks are run
    record._new_record_before_last_commit = true if earlier_saved_candidate&._new_record_before_last_commit

    # The last instance to save itself is likeliest to have internal
    # state that matches what's committed to the database
    candidates[record] = record
  end
end

#records

[ GitHub ]

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

def records
  if @lazy_enrollment_records
    @records.concat @lazy_enrollment_records.values
    @lazy_enrollment_records = nil
  end
  @records
end

#restore!

[ GitHub ]

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

def restore!
  if materialized?
    incomplete!
    @materialized = false
    materialize!
  end
end

#rollback_records

[ GitHub ]

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

def rollback_records
  if records
    begin
      ite = unique_records

      instances_to_run_callbacks_on = prepare_instances_to_run_callbacks_on(ite)

      run_action_on_records(ite, instances_to_run_callbacks_on) do |record, should_run_callbacks|
        record.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: should_run_callbacks)
      end
    ensure
      ite&.each do |i|
        i.rolledback!(force_restore_state: full_rollback?, should_run_callbacks: false)
      end
    end
  end

  @callbacks&.each(&:after_rollback)
end

#run_action_on_records(records, instances_to_run_callbacks_on) (private)

[ GitHub ]

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

def run_action_on_records(records, instances_to_run_callbacks_on)
  while record = records.shift
    should_run_callbacks = record.__id__ == instances_to_run_callbacks_on[record].__id__

    yield record, should_run_callbacks
  end
end

#unique_records (private)

[ GitHub ]

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

def unique_records
  records.uniq(&:__id__)
end