Module: ActiveRecordReplica

Defined in:
lib/active_record_replica/active_record_replica.rb,
lib/active_record_replica/errors.rb,
lib/active_record_replica/railtie.rb,
lib/active_record_replica/replica.rb,
lib/active_record_replica/version.rb,
lib/active_record_replica/extensions.rb

Overview

ActiveRecord read from a replica

Defined Under Namespace

Modules: Extensions Classes: Railtie, Replica, TransactionAttempted

Constant Summary collapse

VERSION =
'2.0.1'
SELECT_METHODS =

Select Methods

[:select, :select_all, :select_one, :select_rows, :select_value, :select_values]

Class Method Summary collapse

Class Method Details

.block_transactionsObject

When only reading from a replica it is important to prevent entering any into a transaction since the transaction still sends traffic to the primary that will cause the primary database to slow down processing empty transactions.



73
74
75
76
77
78
79
80
81
# File 'lib/active_record_replica/active_record_replica.rb', line 73

def self.block_transactions
  begin
    previous = Thread.current.thread_variable_get(:active_record_replica_transaction)
    Thread.current.thread_variable_set(:active_record_replica_transaction, :block)
    yield
  ensure
    Thread.current.thread_variable_set(:active_record_replica_transaction, previous)
  end
end

.block_transactions?Boolean

Whether any attempt to start a transaction should result in an exception

Returns:

  • (Boolean)


117
118
119
# File 'lib/active_record_replica/active_record_replica.rb', line 117

def self.block_transactions?
  Thread.current.thread_variable_get(:active_record_replica_transaction) == :block
end

.ignore_transactions=(ignore_transactions) ⇒ Object

Set whether replica reads should ignore transactions



132
133
134
# File 'lib/active_record_replica/active_record_replica.rb', line 132

def self.ignore_transactions=(ignore_transactions)
  @ignore_transactions = ignore_transactions
end

.ignore_transactions?Boolean

Returns whether replica reads are ignoring transactions

Returns:

  • (Boolean)


127
128
129
# File 'lib/active_record_replica/active_record_replica.rb', line 127

def self.ignore_transactions?
  @ignore_transactions
end

.install!(adapter_class = nil, environment = nil) ⇒ Object

Install ActiveRecord::Replica into ActiveRecord to redirect reads to the replica Parameters:

adapter_class:
  By default, only the default Database adapter (ActiveRecord::Base.connection.class)
  is extended with replica read capabilities

environment:
  In a non-Rails environment, supply the environment such as
  'development', 'production'


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/active_record_replica/active_record_replica.rb', line 21

def self.install!(adapter_class = nil, environment = nil)
  replica_config =
    if ActiveRecord::Base.connection.respond_to?(:config)
      ActiveRecord::Base.connection.config[:replica]
    else
      ActiveRecord::Base.configurations[environment || Rails.env]['replica']
    end
  if replica_config
    ActiveRecord::Base.logger.info "ActiveRecordReplica.install! v#{ActiveRecordReplica::VERSION} Establishing connection to replica database"
    Replica.establish_connection(replica_config)

    # Inject a new #select method into the ActiveRecord Database adapter
    base = adapter_class || ActiveRecord::Base.connection.class
    base.include(Extensions)
  else
    ActiveRecord::Base.logger.info "ActiveRecordReplica not installed since no replica database defined"
  end
end

.read_from_primaryObject

Force reads for the supplied block to read from the primary database Only applies to calls made within the current thread



42
43
44
45
46
47
48
49
50
51
# File 'lib/active_record_replica/active_record_replica.rb', line 42

def self.read_from_primary
  return yield if read_from_primary?
  begin
    previous = Thread.current.thread_variable_get(:active_record_replica)
    read_from_primary!
    yield
  ensure
    Thread.current.thread_variable_set(:active_record_replica, previous)
  end
end

.read_from_primary!Object

Force all subsequent reads on this thread and any fibers called by this thread to go the primary



107
108
109
# File 'lib/active_record_replica/active_record_replica.rb', line 107

def self.read_from_primary!
  Thread.current.thread_variable_set(:active_record_replica, :primary)
end

.read_from_primary?Boolean

Whether this thread is currently forcing all reads to go against the primary database

Returns:

  • (Boolean)


97
98
99
# File 'lib/active_record_replica/active_record_replica.rb', line 97

def self.read_from_primary?
  Thread.current.thread_variable_get(:active_record_replica) == :primary
end

.read_from_replicaObject

The default behavior can also set to read/write operations against primary Create an initializer file config/initializer/active_record_replica.rb and set ActiveRecordReplica.read_from_primary! to force read from primary. Then use this method and supply block to read from the replica database Only applies to calls made within the current thread



59
60
61
62
63
64
65
66
67
68
# File 'lib/active_record_replica/active_record_replica.rb', line 59

def self.read_from_replica
  return yield if read_from_replica?
  begin
    previous = Thread.current.thread_variable_get(:active_record_replica)
    read_from_replica!
    yield
  ensure
    Thread.current.thread_variable_set(:active_record_replica, previous)
  end
end

.read_from_replica!Object

Subsequent reads on this thread and any fibers called by this thread can go to a replica



112
113
114
# File 'lib/active_record_replica/active_record_replica.rb', line 112

def self.read_from_replica!
  Thread.current.thread_variable_set(:active_record_replica, nil)
end

.read_from_replica?Boolean

Whether this thread is currently forcing all reads to go against the replica database

Returns:

  • (Boolean)


102
103
104
# File 'lib/active_record_replica/active_record_replica.rb', line 102

def self.read_from_replica?
  Thread.current.thread_variable_get(:active_record_replica).nil?
end

.skip_transactionsObject

During this block any attempts to start or end transactions will be ignored. This extreme action should only be taken when 100% certain no writes are going to be performed.



86
87
88
89
90
91
92
93
94
# File 'lib/active_record_replica/active_record_replica.rb', line 86

def self.skip_transactions
  begin
    previous = Thread.current.thread_variable_get(:active_record_replica_transaction)
    Thread.current.thread_variable_set(:active_record_replica_transaction, :skip)
    yield
  ensure
    Thread.current.thread_variable_set(:active_record_replica_transaction, previous)
  end
end

.skip_transactions?Boolean

Whether any attempt to start a transaction should be skipped.

Returns:

  • (Boolean)


122
123
124
# File 'lib/active_record_replica/active_record_replica.rb', line 122

def self.skip_transactions?
  Thread.current.thread_variable_get(:active_record_replica_transaction) == :skip
end