Module: ActiveRecordShards::DefaultReplicaPatches

Defined in:
lib/active_record_shards/default_replica_patches.rb

Defined Under Namespace

Modules: ActiveRelationPatches, AssociationsAssociationAssociationScopePatch, AssociationsAssociationFindTargetPatch, AssociationsPreloaderAssociationAssociatedRecordsByOwnerPatch, AssociationsPreloaderAssociationLoadRecordsPatch, InstanceMethods, Rails41HasAndBelongsToManyBuilderExtension, Rails52RelationPatches, SchemaDefinePatch, TypeCasterConnectionConnectionPatch

Constant Summary collapse

CLASS_REPLICA_METHODS =
[
  :calculate,
  :count_by_sql,
  :exists?,
  :find,
  :find_by,
  :find_by_sql,
  :find_every,
  :find_one,
  :find_some,
  :get_primary_key
].freeze
CLASS_FORCE_REPLICA_METHODS =
[
  :replace_bind_variable,
  :replace_bind_variables,
  :sanitize_sql_array,
  :sanitize_sql_hash_for_assignment,
  :table_exists?
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(base) ⇒ Object



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

def self.extended(base)
  CLASS_REPLICA_METHODS.each { |m| ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(true, base, m) }
  CLASS_FORCE_REPLICA_METHODS.each { |m| ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(true, base, m, force_on_replica: true) }

  ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(true, base, :load_schema!, force_on_replica: true)
  ActiveRecordShards::DefaultReplicaPatches.wrap_method_in_on_replica(false, base, :reload)

  base.class_eval do
    include InstanceMethods
  end
end

.wrap_method_in_on_replica(class_method, base, method, force_on_replica: false) ⇒ Object



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/active_record_shards/default_replica_patches.rb', line 5

def self.wrap_method_in_on_replica(class_method, base, method, force_on_replica: false)
  base_methods =
    if class_method
      base.methods + base.private_methods
    else
      base.instance_methods + base.private_instance_methods
    end

  return unless base_methods.include?(method)

  _, method, punctuation = method.to_s.match(/^(.*?)([\?\!]?)$/).to_a
  # _ALWAYS_ on replica, or only for on `on_replica_by_default = true` models?
  wrapper = force_on_replica ? 'force_on_replica' : 'on_replica_unless_tx'
  base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
    #{class_method ? 'class << self' : ''}
      def #{method}_with_default_replica#{punctuation}(*args, &block)
        #{wrapper} do
          #{method}_without_default_replica#{punctuation}(*args, &block)
        end
      end
      ruby2_keywords(:#{method}_with_default_replica#{punctuation}) if respond_to?(:ruby2_keywords, true)
      alias_method :#{method}_without_default_replica#{punctuation}, :#{method}#{punctuation}
      alias_method :#{method}#{punctuation}, :#{method}_with_default_replica#{punctuation}
    #{class_method ? 'end' : ''}
  RUBY
end

Instance Method Details

#_in_transaction?Boolean

Returns:

  • (Boolean)


82
83
84
# File 'lib/active_record_shards/default_replica_patches.rb', line 82

def _in_transaction?
  connected? && connection.transaction_open?
end

#force_on_replica(&block) ⇒ Object



86
87
88
89
90
# File 'lib/active_record_shards/default_replica_patches.rb', line 86

def force_on_replica(&block)
  return yield if Thread.current[:_active_record_shards_in_migration]

  on_cx_switch_block(:replica, construct_ro_scope: false, force: true, &block)
end

#on_replica_unless_tx(&block) ⇒ Object



71
72
73
74
75
76
77
78
79
80
# File 'lib/active_record_shards/default_replica_patches.rb', line 71

def on_replica_unless_tx(&block)
  return yield if Thread.current[:_active_record_shards_in_migration]
  return yield if _in_transaction?

  if on_replica_by_default?
    on_replica(&block)
  else
    yield
  end
end