Class: DerailSpecs::Transaction

Inherits:
Object
  • Object
show all
Defined in:
lib/derail_specs/transaction.rb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.beginObject



7
8
9
# File 'lib/derail_specs/transaction.rb', line 7

def self.begin
  instance.begin
end

.instanceObject



3
4
5
# File 'lib/derail_specs/transaction.rb', line 3

def self.instance
  @instance ||= new
end

.resetObject



56
57
58
# File 'lib/derail_specs/transaction.rb', line 56

def self.reset
  instance.reset
end

.rollbackObject



38
39
40
# File 'lib/derail_specs/transaction.rb', line 38

def self.rollback
  instance.rollback
end

Instance Method Details

#beginObject



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/derail_specs/transaction.rb', line 11

def begin
  @connections = gather_connections
  @connections.each do |connection|
    connection.begin_transaction joinable: false, _lazy: false
    connection.pool.lock_thread = true
  end

  # When connections are established in the future, begin a transaction too
  @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
    if payload.key?(:spec_name) && (spec_name = payload[:spec_name])
      setup_shared_connection_pool

      begin
        ActiveRecord::Base.connection_handler.retrieve_connection(spec_name)
      rescue ActiveRecord::ConnectionNotEstablished
        connection = nil
      end

      if connection && !@connections.include?(connection)
        connection.begin_transaction joinable: false, _lazy: false
        connection.pool.lock_thread = true
        @connections << connection
      end
    end
  end
end

#gather_connectionsObject



65
66
67
68
69
# File 'lib/derail_specs/transaction.rb', line 65

def gather_connections
  setup_shared_connection_pool if ActiveRecord::VERSION::MAJOR >= 6

  ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
end

#resetObject



60
61
62
63
# File 'lib/derail_specs/transaction.rb', line 60

def reset
  rollback
  self.begin
end

#rollbackObject



42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/derail_specs/transaction.rb', line 42

def rollback
  return unless @connections.present?

  ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber

  @connections.each do |connection|
    connection.rollback_transaction if connection.transaction_open?
    connection.pool.lock_thread = false
  end
  @connections.clear

  ActiveRecord::Base.clear_active_connections!
end

#setup_shared_connection_poolObject

Shares the writing connection pool with connections on other handlers.

In an application with a primary and replica the test fixtures need to share a connection pool so that the reading connection can see data in the open transaction on the writing connection.



77
78
79
80
81
82
83
84
85
# File 'lib/derail_specs/transaction.rb', line 77

def setup_shared_connection_pool
  @legacy_saved_pool_configs ||= Hash.new { |hash, key| hash[key] = {} }
  @saved_pool_configs ||= Hash.new { |hash, key| hash[key] = {} }

  ActiveRecord::TestFixtures
    .instance_method(:setup_shared_connection_pool)
    .bind(self)
    .call
end