active_record_shards 3.22.0 → 4.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +57 -126
- data/lib/active_record_shards/association_collection_connection_selection.rb +14 -23
- data/lib/active_record_shards/configuration_parser.rb +6 -30
- data/lib/active_record_shards/connection_switcher-5-0.rb +0 -10
- data/lib/active_record_shards/connection_switcher-5-1.rb +0 -10
- data/lib/active_record_shards/connection_switcher.rb +71 -115
- data/lib/active_record_shards/default_slave_patches.rb +134 -3
- data/lib/active_record_shards/model.rb +16 -35
- data/lib/active_record_shards/shard_selection.rb +27 -66
- data/lib/active_record_shards/shard_support.rb +0 -1
- data/lib/active_record_shards/sharded_model.rb +15 -0
- data/lib/active_record_shards/sql_comments.rb +4 -11
- data/lib/active_record_shards/tasks.rb +22 -41
- data/lib/active_record_shards.rb +6 -126
- metadata +64 -74
- data/lib/active_record_shards/connection_handler.rb +0 -8
- data/lib/active_record_shards/connection_pool.rb +0 -36
- data/lib/active_record_shards/connection_specification.rb +0 -19
- data/lib/active_record_shards/connection_switcher-4-2.rb +0 -56
- data/lib/active_record_shards/connection_switcher-6-0.rb +0 -30
- data/lib/active_record_shards/default_replica_patches.rb +0 -278
- data/lib/active_record_shards/deprecation.rb +0 -12
- data/lib/active_record_shards/migration.rb +0 -125
- data/lib/active_record_shards/patches-4-2.rb +0 -9
- data/lib/active_record_shards/schema_dumper_extension.rb +0 -42
@@ -1,34 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
2
|
require 'active_record_shards/shard_support'
|
4
3
|
|
5
4
|
module ActiveRecordShards
|
6
5
|
module ConnectionSwitcher
|
7
|
-
SHARD_NAMES_CONFIG_KEY = 'shard_names'
|
6
|
+
SHARD_NAMES_CONFIG_KEY = 'shard_names'.freeze
|
8
7
|
|
9
8
|
def self.extended(base)
|
10
|
-
|
11
|
-
|
12
|
-
base.singleton_class.send(:alias_method, :load_schema!, :load_schema_with_default_shard!)
|
13
|
-
else
|
14
|
-
base.singleton_class.send(:alias_method, :columns_without_default_shard, :columns)
|
15
|
-
base.singleton_class.send(:alias_method, :columns, :columns_with_default_shard)
|
16
|
-
end
|
9
|
+
base.singleton_class.send(:alias_method, :load_schema_without_default_shard!, :load_schema!)
|
10
|
+
base.singleton_class.send(:alias_method, :load_schema!, :load_schema_with_default_shard!)
|
17
11
|
|
18
12
|
base.singleton_class.send(:alias_method, :table_exists_without_default_shard?, :table_exists?)
|
19
13
|
base.singleton_class.send(:alias_method, :table_exists?, :table_exists_with_default_shard?)
|
20
|
-
|
21
|
-
base.singleton_class.send(:alias_method, :reset_primary_key_without_default_shard, :reset_primary_key)
|
22
|
-
base.singleton_class.send(:alias_method, :reset_primary_key, :reset_primary_key_with_default_shard)
|
23
|
-
end
|
24
|
-
|
25
|
-
def default_shard=(new_default_shard)
|
26
|
-
ActiveRecordShards::ShardSelection.default_shard = new_default_shard
|
27
|
-
switch_connection(shard: new_default_shard)
|
28
|
-
end
|
29
|
-
|
30
|
-
def on_primary_db(&block)
|
31
|
-
on_shard(nil, &block)
|
32
14
|
end
|
33
15
|
|
34
16
|
def on_shard(shard)
|
@@ -39,9 +21,9 @@ module ActiveRecordShards
|
|
39
21
|
switch_connection(old_options)
|
40
22
|
end
|
41
23
|
|
42
|
-
def on_first_shard
|
24
|
+
def on_first_shard
|
43
25
|
shard_name = shard_names.first
|
44
|
-
on_shard(shard_name
|
26
|
+
on_shard(shard_name) { yield }
|
45
27
|
end
|
46
28
|
|
47
29
|
def shards
|
@@ -62,93 +44,88 @@ module ActiveRecordShards
|
|
62
44
|
switch_connection(old_options)
|
63
45
|
end
|
64
46
|
|
65
|
-
def
|
66
|
-
condition ?
|
47
|
+
def on_slave_if(condition, &block)
|
48
|
+
condition ? on_slave(&block) : yield
|
67
49
|
end
|
68
|
-
alias_method :on_slave_if, :on_replica_if
|
69
50
|
|
70
|
-
def
|
71
|
-
|
51
|
+
def on_slave_unless(condition, &block)
|
52
|
+
on_slave_if(!condition, &block)
|
72
53
|
end
|
73
|
-
alias_method :on_slave_unless, :on_replica_unless
|
74
54
|
|
75
|
-
def
|
76
|
-
condition ?
|
55
|
+
def on_master_if(condition, &block)
|
56
|
+
condition ? on_master(&block) : yield
|
77
57
|
end
|
78
|
-
alias_method :on_master_if, :on_primary_if
|
79
58
|
|
80
|
-
def
|
81
|
-
|
59
|
+
def on_master_unless(condition, &block)
|
60
|
+
on_master_if(!condition, &block)
|
82
61
|
end
|
83
|
-
alias_method :on_master_unless, :on_primary_unless
|
84
62
|
|
85
|
-
def
|
63
|
+
def on_master_or_slave(which, &block)
|
86
64
|
if block_given?
|
87
65
|
on_cx_switch_block(which, &block)
|
88
66
|
else
|
89
|
-
|
67
|
+
MasterSlaveProxy.new(self, which)
|
90
68
|
end
|
91
69
|
end
|
92
|
-
alias_method :on_master_or_slave, :on_primary_or_replica
|
93
70
|
|
94
|
-
# Executes queries using the
|
95
|
-
# if you want to execute a block of code on the
|
96
|
-
# Account.
|
71
|
+
# Executes queries using the slave database. Fails over to master if no slave is found.
|
72
|
+
# if you want to execute a block of code on the slave you can go:
|
73
|
+
# Account.on_slave do
|
97
74
|
# Account.first
|
98
75
|
# end
|
99
|
-
# the first account will be found on the
|
76
|
+
# the first account will be found on the slave DB
|
100
77
|
#
|
101
78
|
# For one-liners you can simply do
|
102
|
-
# Account.
|
103
|
-
def
|
104
|
-
|
79
|
+
# Account.on_slave.first
|
80
|
+
def on_slave(&block)
|
81
|
+
on_master_or_slave(:slave, &block)
|
105
82
|
end
|
106
|
-
alias_method :on_slave, :on_replica
|
107
83
|
|
108
|
-
def
|
109
|
-
|
84
|
+
def on_master(&block)
|
85
|
+
on_master_or_slave(:master, &block)
|
110
86
|
end
|
111
|
-
alias_method :on_master, :on_primary
|
112
|
-
|
113
|
-
# just to ease the transition from replica to active_record_shards
|
114
|
-
alias_method :with_slave, :on_replica
|
115
|
-
alias_method :with_slave_if, :on_replica_if
|
116
|
-
alias_method :with_slave_unless, :on_replica_unless
|
117
87
|
|
118
88
|
def on_cx_switch_block(which, force: false, construct_ro_scope: nil, &block)
|
119
|
-
@
|
120
|
-
@
|
89
|
+
@disallow_slave ||= 0
|
90
|
+
@disallow_slave += 1 if which == :master
|
121
91
|
|
122
|
-
|
123
|
-
|
124
|
-
switch_to_replica = force || @disallow_replica.zero?
|
92
|
+
switch_to_slave = force || @disallow_slave.zero?
|
125
93
|
old_options = current_shard_selection.options
|
126
94
|
|
127
|
-
switch_connection(
|
95
|
+
switch_connection(slave: switch_to_slave)
|
128
96
|
|
129
97
|
# we avoid_readonly_scope to prevent some stack overflow problems, like when
|
130
98
|
# .columns calls .with_scope which calls .columns and onward, endlessly.
|
131
|
-
if self == ActiveRecord::Base || !
|
99
|
+
if self == ActiveRecord::Base || !switch_to_slave || construct_ro_scope == false
|
132
100
|
yield
|
133
101
|
else
|
134
102
|
readonly.scoping(&block)
|
135
103
|
end
|
136
104
|
ensure
|
137
|
-
@
|
105
|
+
@disallow_slave -= 1 if which == :master
|
138
106
|
switch_connection(old_options) if old_options
|
139
107
|
end
|
140
108
|
|
109
|
+
def connection_specification_name
|
110
|
+
name = current_shard_selection.resolve_connection_name(sharded: is_sharded?, configurations: configurations)
|
111
|
+
|
112
|
+
unless configurations[name] || name == "primary"
|
113
|
+
raise ActiveRecord::AdapterNotSpecified, "No database defined by #{name} in your database config. (configurations: #{configurations.inspect})"
|
114
|
+
end
|
115
|
+
|
116
|
+
name
|
117
|
+
end
|
118
|
+
|
141
119
|
def supports_sharding?
|
142
120
|
shard_names.any?
|
143
121
|
end
|
144
122
|
|
145
|
-
def
|
146
|
-
current_shard_selection.
|
123
|
+
def on_slave?
|
124
|
+
current_shard_selection.on_slave?
|
147
125
|
end
|
148
|
-
alias_method :on_slave?, :on_replica?
|
149
126
|
|
150
127
|
def current_shard_selection
|
151
|
-
Thread.current[:shard_selection] ||= ShardSelection.new
|
128
|
+
Thread.current[:shard_selection] ||= ShardSelection.new(shard_names.first)
|
152
129
|
end
|
153
130
|
|
154
131
|
def current_shard_id
|
@@ -156,99 +133,78 @@ module ActiveRecordShards
|
|
156
133
|
end
|
157
134
|
|
158
135
|
def shard_names
|
159
|
-
unless
|
160
|
-
raise "
|
136
|
+
unless config = configurations[shard_env]
|
137
|
+
raise "Did not find #{shard_env} in configurations, did you forget to add it to your database config? (configurations: #{configurations.inspect})"
|
161
138
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
139
|
+
unless config[SHARD_NAMES_CONFIG_KEY]
|
140
|
+
raise "No shards configured for #{shard_env}"
|
141
|
+
end
|
142
|
+
unless config[SHARD_NAMES_CONFIG_KEY].all? { |shard_name| shard_name.is_a?(Integer) }
|
143
|
+
raise "All shard names must be integers: #{config[SHARD_NAMES_CONFIG_KEY].inspect}."
|
144
|
+
end
|
145
|
+
config[SHARD_NAMES_CONFIG_KEY]
|
168
146
|
end
|
169
147
|
|
170
148
|
private
|
171
149
|
|
172
|
-
def config_for_env
|
173
|
-
@_ars_config_for_env ||= {}
|
174
|
-
@_ars_config_for_env[shard_env] ||= begin
|
175
|
-
unless config = configurations[shard_env]
|
176
|
-
raise "Did not find #{shard_env} in configurations, did you forget to add it to your database config? (configurations: #{configurations.to_h.keys.inspect})"
|
177
|
-
end
|
178
|
-
|
179
|
-
config
|
180
|
-
end
|
181
|
-
end
|
182
|
-
alias_method :check_config_for_env, :config_for_env
|
183
|
-
|
184
150
|
def switch_connection(options)
|
185
151
|
if options.any?
|
186
|
-
if options.key?(:replica)
|
187
|
-
current_shard_selection.on_replica = options[:replica]
|
188
|
-
end
|
189
|
-
|
190
152
|
if options.key?(:shard)
|
191
|
-
|
192
|
-
|
153
|
+
unless config = configurations[shard_env]
|
154
|
+
raise "Did not find #{shard_env} in configurations, did you forget to add it to your database config? (configurations: #{configurations.inspect})"
|
155
|
+
end
|
156
|
+
unless config['shard_names'].include?(options[:shard])
|
157
|
+
raise "Did not find shard #{options[:shard]} in configurations"
|
158
|
+
end
|
193
159
|
current_shard_selection.shard = options[:shard]
|
194
160
|
end
|
195
161
|
|
162
|
+
if options.key?(:slave)
|
163
|
+
current_shard_selection.on_slave = options[:slave]
|
164
|
+
end
|
165
|
+
|
196
166
|
ensure_shard_connection
|
197
167
|
end
|
198
168
|
end
|
199
169
|
|
200
170
|
def shard_env
|
201
|
-
ActiveRecordShards.
|
171
|
+
ActiveRecordShards.rails_env
|
202
172
|
end
|
203
173
|
|
204
|
-
|
205
|
-
# a shard.
|
206
|
-
def with_default_shard(&block)
|
174
|
+
def with_default_shard
|
207
175
|
if is_sharded? && current_shard_id.nil? && table_name != ActiveRecord::SchemaMigration.table_name
|
208
|
-
on_first_shard
|
176
|
+
on_first_shard { yield }
|
209
177
|
else
|
210
178
|
yield
|
211
179
|
end
|
212
180
|
end
|
213
181
|
|
214
|
-
|
215
|
-
|
216
|
-
with_default_shard { load_schema_without_default_shard! }
|
217
|
-
end
|
218
|
-
else
|
219
|
-
def columns_with_default_shard
|
220
|
-
with_default_shard { columns_without_default_shard }
|
221
|
-
end
|
182
|
+
def load_schema_with_default_shard!
|
183
|
+
with_default_shard { load_schema_without_default_shard! }
|
222
184
|
end
|
223
185
|
|
224
186
|
def table_exists_with_default_shard?
|
225
187
|
with_default_shard { table_exists_without_default_shard? }
|
226
188
|
end
|
227
189
|
|
228
|
-
class
|
190
|
+
class MasterSlaveProxy
|
229
191
|
def initialize(target, which)
|
230
192
|
@target = target
|
231
193
|
@which = which
|
232
194
|
end
|
233
195
|
|
234
|
-
def method_missing(method, *args, &block) # rubocop:disable Style/
|
235
|
-
@target.
|
196
|
+
def method_missing(method, *args, &block) # rubocop:disable Style/MethodMissing
|
197
|
+
@target.on_master_or_slave(@which) { @target.send(method, *args, &block) }
|
236
198
|
end
|
237
199
|
end
|
238
|
-
|
239
|
-
MasterSlaveProxy = PrimaryReplicaProxy
|
240
200
|
end
|
241
201
|
end
|
242
202
|
|
243
203
|
case "#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"
|
244
|
-
when '4.2'
|
245
|
-
require 'active_record_shards/connection_switcher-4-2'
|
246
204
|
when '5.0'
|
247
205
|
require 'active_record_shards/connection_switcher-5-0'
|
248
|
-
when '5.1'
|
206
|
+
when '5.1'
|
249
207
|
require 'active_record_shards/connection_switcher-5-1'
|
250
|
-
when '6.0'
|
251
|
-
require 'active_record_shards/connection_switcher-6-0'
|
252
208
|
else
|
253
209
|
raise "ActiveRecordShards is not compatible with #{ActiveRecord::VERSION::STRING}"
|
254
210
|
end
|
@@ -1,5 +1,136 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
3
2
|
module ActiveRecordShards
|
4
|
-
DefaultSlavePatches
|
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
|
+
on_cx_switch_block(:slave, construct_ro_scope: false, force: true) do
|
30
|
+
columns_without_force_slave(*args, &block)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def table_exists_with_force_slave?(*args, &block)
|
35
|
+
on_cx_switch_block(:slave, construct_ro_scope: false, force: true) 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
|
5
136
|
end
|
@@ -3,59 +3,44 @@
|
|
3
3
|
module ActiveRecordShards
|
4
4
|
module Model
|
5
5
|
def not_sharded
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
self.sharded = false
|
6
|
+
ActiveSupport::Deprecation.warn("Calling not_sharded is deprecated. "\
|
7
|
+
"Please ensure to still read from the "\
|
8
|
+
"account db slave after removing the "\
|
9
|
+
"call.")
|
11
10
|
end
|
12
11
|
|
13
12
|
def is_sharded? # rubocop:disable Naming/PredicateName
|
14
|
-
|
15
|
-
sharded != false && supports_sharding?
|
16
|
-
elsif self == base_class
|
17
|
-
if sharded.nil?
|
18
|
-
ActiveRecord::Base.is_sharded?
|
19
|
-
else
|
20
|
-
sharded != false
|
21
|
-
end
|
22
|
-
else
|
23
|
-
base_class.is_sharded?
|
24
|
-
end
|
13
|
+
false
|
25
14
|
end
|
26
15
|
|
27
|
-
def
|
16
|
+
def on_slave_by_default?
|
28
17
|
if self == ActiveRecord::Base
|
29
18
|
false
|
30
19
|
else
|
31
20
|
base = base_class
|
32
|
-
if base.instance_variable_defined?(:@
|
33
|
-
base.instance_variable_get(:@
|
21
|
+
if base.instance_variable_defined?(:@on_slave_by_default)
|
22
|
+
base.instance_variable_get(:@on_slave_by_default)
|
34
23
|
end
|
35
24
|
end
|
36
25
|
end
|
37
|
-
alias_method :on_slave_by_default?, :on_replica_by_default?
|
38
26
|
|
39
|
-
def
|
27
|
+
def on_slave_by_default=(value)
|
40
28
|
if self == ActiveRecord::Base
|
41
|
-
raise ArgumentError, "Cannot set
|
29
|
+
raise ArgumentError, "Cannot set on_slave_by_default on ActiveRecord::Base"
|
42
30
|
else
|
43
|
-
base_class.instance_variable_set(:@
|
31
|
+
base_class.instance_variable_set(:@on_slave_by_default, value)
|
44
32
|
end
|
45
33
|
end
|
46
|
-
alias_method :on_slave_by_default=, :on_replica_by_default=
|
47
34
|
|
48
35
|
module InstanceMethods
|
49
|
-
def
|
50
|
-
@
|
36
|
+
def initialize_shard_and_slave
|
37
|
+
@from_slave = !!self.class.current_shard_selection.options[:slave]
|
51
38
|
@from_shard = self.class.current_shard_selection.options[:shard]
|
52
39
|
end
|
53
|
-
alias_method :initialize_shard_and_slave, :initialize_shard_and_replica
|
54
40
|
|
55
|
-
def
|
56
|
-
@
|
41
|
+
def from_slave?
|
42
|
+
@from_slave
|
57
43
|
end
|
58
|
-
alias_method :from_slave?, :from_replica?
|
59
44
|
|
60
45
|
def from_shard
|
61
46
|
@from_shard
|
@@ -64,11 +49,7 @@ module ActiveRecordShards
|
|
64
49
|
|
65
50
|
def self.extended(base)
|
66
51
|
base.send(:include, InstanceMethods)
|
67
|
-
base.after_initialize :
|
52
|
+
base.after_initialize :initialize_shard_and_slave
|
68
53
|
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
attr_accessor :sharded
|
73
54
|
end
|
74
55
|
end
|
@@ -1,91 +1,52 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
2
|
module ActiveRecordShards
|
4
3
|
class ShardSelection
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
@on_replica = false
|
10
|
-
@shard = nil
|
4
|
+
def initialize(shard)
|
5
|
+
@on_slave = false
|
6
|
+
self.shard = shard
|
11
7
|
end
|
12
8
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
if @shard == NO_SHARD
|
18
|
-
nil
|
19
|
-
else
|
20
|
-
@shard || self.class.default_shard
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def shard_name(klass = nil, try_replica = true)
|
26
|
-
the_shard = shard(klass)
|
27
|
-
|
28
|
-
@shard_names ||= {}
|
29
|
-
@shard_names[ActiveRecordShards.app_env] ||= {}
|
30
|
-
@shard_names[ActiveRecordShards.app_env][the_shard] ||= {}
|
31
|
-
@shard_names[ActiveRecordShards.app_env][the_shard][try_replica] ||= {}
|
32
|
-
@shard_names[ActiveRecordShards.app_env][the_shard][try_replica][@on_replica] ||= begin
|
33
|
-
s = ActiveRecordShards.app_env.dup
|
34
|
-
s << "_shard_#{the_shard}" if the_shard
|
35
|
-
s << "_replica" if @on_replica && try_replica
|
36
|
-
s
|
37
|
-
end
|
38
|
-
end
|
9
|
+
def shard
|
10
|
+
raise "Missing shard information on connection" unless @shard
|
11
|
+
@shard
|
12
|
+
end
|
39
13
|
|
40
|
-
|
14
|
+
PRIMARY = "primary".freeze
|
15
|
+
def resolve_connection_name(sharded:, configurations:)
|
16
|
+
resolved_shard = sharded ? shard : nil
|
41
17
|
|
42
|
-
|
43
|
-
|
44
|
-
nil
|
45
|
-
else
|
46
|
-
@shard || self.class.default_shard
|
47
|
-
end
|
18
|
+
if !resolved_shard && !@on_slave
|
19
|
+
return PRIMARY
|
48
20
|
end
|
49
21
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
22
|
+
@shard_names ||= {}
|
23
|
+
@shard_names[ActiveRecordShards.rails_env] ||= {}
|
24
|
+
@shard_names[ActiveRecordShards.rails_env][resolved_shard] ||= {}
|
25
|
+
@shard_names[ActiveRecordShards.rails_env][resolved_shard][@on_slave] ||= begin
|
26
|
+
s = ActiveRecordShards.rails_env.dup
|
27
|
+
s << "_shard_#{resolved_shard}" if resolved_shard
|
54
28
|
|
55
|
-
@
|
56
|
-
|
57
|
-
@connection_names[env][resolved_shard] ||= {}
|
58
|
-
@connection_names[env][resolved_shard][@on_replica] ||= begin
|
59
|
-
name = env.dup
|
60
|
-
name << "_shard_#{resolved_shard}" if resolved_shard
|
61
|
-
if @on_replica && configurations["#{name}_replica"]
|
62
|
-
"#{name}_replica"
|
63
|
-
else
|
64
|
-
# ActiveRecord always names its default connection pool 'primary'
|
65
|
-
# while everything else is named by the configuration name
|
66
|
-
resolved_shard ? name : PRIMARY
|
67
|
-
end
|
29
|
+
if @on_slave && configurations["#{s}_slave"] # fall back to master connection
|
30
|
+
s << "_slave"
|
68
31
|
end
|
32
|
+
s
|
69
33
|
end
|
70
|
-
|
71
34
|
end
|
72
35
|
|
73
36
|
def shard=(new_shard)
|
74
|
-
@shard = (new_shard
|
37
|
+
@shard = Integer(new_shard)
|
75
38
|
end
|
76
39
|
|
77
|
-
def
|
78
|
-
@
|
40
|
+
def on_slave?
|
41
|
+
@on_slave
|
79
42
|
end
|
80
|
-
alias_method :on_slave?, :on_replica?
|
81
43
|
|
82
|
-
def
|
83
|
-
@
|
44
|
+
def on_slave=(new_slave)
|
45
|
+
@on_slave = (new_slave == true)
|
84
46
|
end
|
85
|
-
alias_method :on_slave=, :on_replica=
|
86
47
|
|
87
48
|
def options
|
88
|
-
{ shard: @shard,
|
49
|
+
{ shard: @shard, slave: @on_slave }
|
89
50
|
end
|
90
51
|
end
|
91
52
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ActiveRecordShards
|
2
|
+
module Ext
|
3
|
+
module ShardedModel
|
4
|
+
def is_sharded? # rubocop:disable Naming/PredicateName
|
5
|
+
true
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class ShardedModel < ActiveRecord::Base
|
11
|
+
self.abstract_class = true
|
12
|
+
|
13
|
+
extend ActiveRecordShards::Ext::ShardedModel
|
14
|
+
end
|
15
|
+
end
|