active_record_shards 4.0.0.beta9 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,136 +0,0 @@
1
- # frozen_string_literal: true
2
- module ActiveRecordShards
3
- module DefaultSlavePatches
4
- def self.wrap_method_in_on_slave(class_method, base, method)
5
- base_methods =
6
- if class_method
7
- base.methods + base.private_methods
8
- else
9
- base.instance_methods + base.private_instance_methods
10
- end
11
-
12
- return unless base_methods.include?(method)
13
- _, method, punctuation = method.to_s.match(/^(.*?)([\?\!]?)$/).to_a
14
- base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
15
- #{class_method ? 'class << self' : ''}
16
- def #{method}_with_default_slave#{punctuation}(*args, &block)
17
- on_slave_unless_tx do
18
- #{method}_without_default_slave#{punctuation}(*args, &block)
19
- end
20
- end
21
-
22
- alias_method :#{method}_without_default_slave#{punctuation}, :#{method}#{punctuation}
23
- alias_method :#{method}#{punctuation}, :#{method}_with_default_slave#{punctuation}
24
- #{class_method ? 'end' : ''}
25
- RUBY
26
- end
27
-
28
- def columns_with_force_slave(*args, &block)
29
- force_cx_switch_slave_block do
30
- columns_without_force_slave(*args, &block)
31
- end
32
- end
33
-
34
- def table_exists_with_force_slave?(*args, &block)
35
- force_cx_switch_slave_block do
36
- table_exists_without_force_slave?(*args, &block)
37
- end
38
- end
39
-
40
- def transaction_with_slave_off(*args, &block)
41
- if on_slave_by_default?
42
- begin
43
- old_val = Thread.current[:_active_record_shards_slave_off]
44
- Thread.current[:_active_record_shards_slave_off] = true
45
- transaction_without_slave_off(*args, &block)
46
- ensure
47
- Thread.current[:_active_record_shards_slave_off] = old_val
48
- end
49
- else
50
- transaction_without_slave_off(*args, &block)
51
- end
52
- end
53
-
54
- module InstanceMethods
55
- # fix ActiveRecord to do the right thing, and use our aliased quote_value
56
- def quote_value(*args, &block)
57
- self.class.quote_value(*args, &block)
58
- end
59
-
60
- def reload_with_slave_off(*args, &block)
61
- self.class.on_master { reload_without_slave_off(*args, &block) }
62
- end
63
- end
64
-
65
- CLASS_SLAVE_METHODS = [:find_by_sql, :count_by_sql, :calculate, :find_one, :find_some, :find_every, :exists?].freeze
66
-
67
- def self.extended(base)
68
- CLASS_SLAVE_METHODS.each { |m| ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(true, base, m) }
69
-
70
- base.class_eval do
71
- include InstanceMethods
72
-
73
- alias_method :reload_without_slave_off, :reload
74
- alias_method :reload, :reload_with_slave_off
75
-
76
- class << self
77
- alias_method :columns_without_force_slave, :columns
78
- alias_method :columns, :columns_with_force_slave
79
-
80
- alias_method :table_exists_without_force_slave?, :table_exists?
81
- alias_method :table_exists?, :table_exists_with_force_slave?
82
-
83
- alias_method :transaction_without_slave_off, :transaction
84
- alias_method :transaction, :transaction_with_slave_off
85
- end
86
- end
87
- if ActiveRecord::Associations.const_defined?(:HasAndBelongsToManyAssociation)
88
- ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(false, ActiveRecord::Associations::HasAndBelongsToManyAssociation, :construct_sql)
89
- ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(false, ActiveRecord::Associations::HasAndBelongsToManyAssociation, :construct_find_options!)
90
- end
91
- end
92
-
93
- def on_slave_unless_tx
94
- if on_slave_by_default? && !Thread.current[:_active_record_shards_slave_off]
95
- on_slave { yield }
96
- else
97
- yield
98
- end
99
- end
100
-
101
- module ActiveRelationPatches
102
- def self.included(base)
103
- [:calculate, :exists?, :pluck, :find_with_associations].each do |m|
104
- ActiveRecordShards::DefaultSlavePatches.wrap_method_in_on_slave(false, base, m)
105
- end
106
- end
107
-
108
- def on_slave_unless_tx
109
- @klass.on_slave_unless_tx { yield }
110
- end
111
- end
112
-
113
- # in rails 4.1+, they create a join class that's used to pull in records for HABTM.
114
- # this simplifies the hell out of our existence, because all we have to do is inerit on-slave-by-default
115
- # down from the parent now.
116
- module HasAndBelongsToManyBuilderExtension
117
- def self.included(base)
118
- base.class_eval do
119
- alias_method :through_model_without_inherit_default_slave_from_lhs, :through_model
120
- alias_method :through_model, :through_model_with_inherit_default_slave_from_lhs
121
- end
122
- end
123
-
124
- def through_model_with_inherit_default_slave_from_lhs
125
- model = through_model_without_inherit_default_slave_from_lhs
126
- def model.on_slave_by_default?
127
- left_reflection.klass.on_slave_by_default?
128
- end
129
-
130
- model.extend(ActiveRecordShards::Ext::ShardedModel) if model.left_reflection.klass.is_sharded?
131
-
132
- model
133
- end
134
- end
135
- end
136
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
- module ActiveRecordShards
3
- class NoShardSelection
4
- class NoShardSelected < RuntimeError; end
5
-
6
- def shard
7
- raise NoShardSelected, "Missing shard information on connection"
8
- end
9
-
10
- def connection_config
11
- raise NoShardSelected, "No shard selected, can't connect"
12
- end
13
-
14
- def on_shard?
15
- false
16
- end
17
- end
18
- end
@@ -1,31 +0,0 @@
1
- module ActiveRecordShards
2
- module Ext
3
- module ShardedModel
4
- def self.extended(base)
5
- base.extend(ActiveRecordShards::ConnectionSwitcher)
6
- base.include(InstanceMethods)
7
- base.after_initialize :initialize_shard
8
- end
9
-
10
- def is_sharded? # rubocop:disable Naming/PredicateName
11
- true
12
- end
13
-
14
- module InstanceMethods
15
- def initialize_shard
16
- @from_shard = self.class.current_shard_selection.shard
17
- end
18
-
19
- def from_shard
20
- @from_shard
21
- end
22
- end
23
- end
24
- end
25
-
26
- class ShardedModel < ActiveRecord::Base
27
- self.abstract_class = true
28
-
29
- extend ActiveRecordShards::Ext::ShardedModel
30
- end
31
- end
@@ -1,105 +0,0 @@
1
- module ActiveRecordShards
2
- module SlaveDb
3
- def on_slave_if(condition, &block)
4
- condition ? on_slave(&block) : yield
5
- end
6
-
7
- def on_slave_unless(condition, &block)
8
- on_slave_if(!condition, &block)
9
- end
10
-
11
- def on_master_if(condition, &block)
12
- condition ? on_master(&block) : yield
13
- end
14
-
15
- def on_master_unless(condition, &block)
16
- on_master_if(!condition, &block)
17
- end
18
-
19
- def on_master_or_slave(which, &block)
20
- if block_given?
21
- on_cx_switch_block(which, &block)
22
- else
23
- MasterSlaveProxy.new(self, which)
24
- end
25
- end
26
-
27
- def on_slave?
28
- current_slave_selection
29
- end
30
-
31
- # Executes queries using the slave database. Fails over to master if no slave is found.
32
- # if you want to execute a block of code on the slave you can go:
33
- # Account.on_slave do
34
- # Account.first
35
- # end
36
- # the first account will be found on the slave DB
37
- #
38
- # For one-liners you can simply do
39
- # Account.on_slave.first
40
- def on_slave(&block)
41
- on_master_or_slave(:slave, &block)
42
- end
43
-
44
- def on_master(&block)
45
- on_master_or_slave(:master, &block)
46
- end
47
-
48
- def force_cx_switch_slave_block
49
- old_options = current_slave_selection
50
- switch_slave_connection(slave: true)
51
- yield
52
- ensure
53
- switch_slave_connection(slave: old_options)
54
- end
55
-
56
- def on_cx_switch_block(which, &block)
57
- @disallow_slave ||= 0
58
- @disallow_slave += 1 if which == :master
59
-
60
- switch_to_slave = @disallow_slave.zero?
61
- old_options = current_slave_selection
62
-
63
- switch_slave_connection(slave: switch_to_slave)
64
-
65
- # we avoid_readonly_scope to prevent some stack overflow problems, like when
66
- # .columns calls .with_scope which calls .columns and onward, endlessly.
67
- if self == ActiveRecord::Base || !switch_to_slave
68
- yield
69
- else
70
- readonly.scoping(&block)
71
- end
72
- ensure
73
- @disallow_slave -= 1 if which == :master
74
- switch_slave_connection(slave: old_options)
75
- end
76
-
77
- def current_slave_selection=(on_slave)
78
- Thread.current[:slave_selection] = on_slave
79
- end
80
-
81
- def current_slave_selection
82
- !!Thread.current[:slave_selection]
83
- end
84
-
85
- def connection_config
86
- super.merge(slave: current_slave_selection)
87
- end
88
-
89
- def switch_slave_connection(options)
90
- self.current_slave_selection = options[:slave]
91
- ensure_shard_connection
92
- end
93
-
94
- class MasterSlaveProxy
95
- def initialize(target, which)
96
- @target = target
97
- @which = which
98
- end
99
-
100
- def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissing
101
- @target.on_master_or_slave(@which) { @target.send(method, *args, &block) }
102
- end
103
- end
104
- end
105
- end