Class: ActiveRecord::ConnectionAdapters::TransactionManager

Inherits:
Object
  • Object
show all
Defined in:
activerecord/lib/active_record/connection_adapters/abstract/transaction.rb

Overview

:nodoc:

Instance Method Summary collapse

Constructor Details

#initialize(connection) ⇒ TransactionManager

Returns a new instance of TransactionManager.



403
404
405
406
407
408
409
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 403

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

Instance Method Details

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



411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 411

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_transactionObject



498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 498

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_transactionObject



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

def current_transaction
  @stack.last || NULL_TRANSACTION
end

#dirty_current_transactionObject



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

def dirty_current_transaction
  current_transaction.dirty!
end

#disable_lazy_transactions!Object



453
454
455
456
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 453

def disable_lazy_transactions!
  materialize_transactions
  @lazy_transactions_enabled = false
end

#enable_lazy_transactions!Object



458
459
460
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 458

def enable_lazy_transactions!
  @lazy_transactions_enabled = true
end

#lazy_transactions_enabled?Boolean

Returns:

  • (Boolean)


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

def lazy_transactions_enabled?
  @lazy_transactions_enabled
end

#materialize_transactionsObject



482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 482

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_transactionsObject



580
581
582
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 580

def open_transactions
  @stack.size
end

#restorable?Boolean

Returns:

  • (Boolean)


478
479
480
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 478

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

#restore_transactionsObject



470
471
472
473
474
475
476
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 470

def restore_transactions
  return false unless restorable?

  @stack.each(&:restore!)

  true
end

#rollback_transaction(transaction = nil) ⇒ Object



515
516
517
518
519
520
521
522
523
524
525
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 515

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) ⇒ Object



527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# File 'activerecord/lib/active_record/connection_adapters/abstract/transaction.rb', line 527

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