switchman 2.1.0 → 2.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/models/switchman/shard.rb +83 -128
- data/lib/switchman/active_record/attribute_methods.rb +39 -18
- data/lib/switchman/active_record/base.rb +11 -11
- data/lib/switchman/active_record/connection_handler.rb +1 -1
- data/lib/switchman/active_record/connection_pool.rb +3 -1
- data/lib/switchman/active_record/log_subscriber.rb +1 -4
- data/lib/switchman/active_record/migration.rb +6 -3
- data/lib/switchman/active_record/persistence.rb +10 -1
- data/lib/switchman/active_record/postgresql_adapter.rb +1 -1
- data/lib/switchman/active_record/query_cache.rb +1 -1
- data/lib/switchman/active_record/query_methods.rb +23 -0
- data/lib/switchman/connection_pool_proxy.rb +1 -1
- data/lib/switchman/engine.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- data/lib/tasks/switchman.rake +3 -2
- metadata +11 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c6658f02bb242697d5c6b9d7e9c57ba3613295fddc1de3d618cb74ed125835aa
|
4
|
+
data.tar.gz: 05fa7e267a2d442d3f9862e041fe6f297e3272693dd15273bfb17ec86187450f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3651b8ad5d882571716507a49e264b90d70a318e436e816e9dbe2ec0b9ff70031f4bfa7fce4e04748bad8e4dad88a36d16eac4afa1b0c61e2f686ed138bb2ef5
|
7
|
+
data.tar.gz: d1d98111fc70a2786768b61856ae4a54ba914275628489d99f12b7de7b0a891d62e7c41189aad2a388941a0c9ce89742d6751c4078ff15b57660d63d390978ce
|
@@ -129,6 +129,11 @@ module Switchman
|
|
129
129
|
cached_shards[id]
|
130
130
|
end
|
131
131
|
|
132
|
+
def preload_cache
|
133
|
+
cached_shards.reverse_merge!(active_shards.values.index_by(&:id))
|
134
|
+
cached_shards.reverse_merge!(all.index_by(&:id))
|
135
|
+
end
|
136
|
+
|
132
137
|
def clear_cache
|
133
138
|
cached_shards.clear
|
134
139
|
end
|
@@ -139,10 +144,9 @@ module Switchman
|
|
139
144
|
# * +categories+ - an array of categories to activate
|
140
145
|
# * +options+ -
|
141
146
|
# :parallel - true/false to execute in parallel, or a integer of how many
|
142
|
-
# sub-processes
|
143
|
-
#
|
144
|
-
#
|
145
|
-
# :max_procs - only run this many parallel processes at a time
|
147
|
+
# sub-processes. Note that parallel invocation currently uses
|
148
|
+
# forking, so should be used sparingly because you cannot get
|
149
|
+
# results back
|
146
150
|
# :exception - :ignore, :raise, :defer (wait until the end and raise the first
|
147
151
|
# error), or a proc
|
148
152
|
def with_each_shard(*args)
|
@@ -163,11 +167,14 @@ module Switchman
|
|
163
167
|
scope, categories = args
|
164
168
|
end
|
165
169
|
|
170
|
+
# back-compat
|
171
|
+
options[:parallel] = options.delete(:max_procs) if options.key?(:max_procs)
|
172
|
+
|
166
173
|
parallel = case options[:parallel]
|
167
174
|
when true
|
168
|
-
|
175
|
+
[Environment.cpu_count || 2, 2].min
|
169
176
|
when false, nil
|
170
|
-
|
177
|
+
1
|
171
178
|
else
|
172
179
|
options[:parallel]
|
173
180
|
end
|
@@ -178,47 +185,19 @@ module Switchman
|
|
178
185
|
scope = scope.order(::Arel.sql("database_server_id IS NOT NULL, database_server_id, id"))
|
179
186
|
end
|
180
187
|
|
181
|
-
if parallel >
|
182
|
-
max_procs = determine_max_procs(options.delete(:max_procs), parallel)
|
188
|
+
if parallel > 1
|
183
189
|
if ::ActiveRecord::Relation === scope
|
184
190
|
# still need a post-uniq, cause the default database server could be NULL or Rails.env in the db
|
185
191
|
database_servers = scope.reorder('database_server_id').select(:database_server_id).distinct.
|
186
192
|
map(&:database_server).compact.uniq
|
187
193
|
# nothing to do
|
188
194
|
return if database_servers.count == 0
|
189
|
-
parallel = [(max_procs.to_f / database_servers.count).ceil, parallel].min if max_procs
|
190
195
|
|
191
196
|
scopes = Hash[database_servers.map do |server|
|
192
|
-
|
193
|
-
if parallel == 1
|
194
|
-
subscopes = [server_scope]
|
195
|
-
else
|
196
|
-
subscopes = []
|
197
|
-
total = server_scope.count
|
198
|
-
ranges = []
|
199
|
-
server_scope.find_ids_in_ranges(:batch_size => (total.to_f / parallel).ceil) do |min, max|
|
200
|
-
ranges << [min, max]
|
201
|
-
end
|
202
|
-
# create a half-open range on the last one
|
203
|
-
ranges.last[1] = nil
|
204
|
-
ranges.each do |min, max|
|
205
|
-
subscope = server_scope.where("id>=?", min)
|
206
|
-
subscope = subscope.where("id<=?", max) if max
|
207
|
-
subscopes << subscope
|
208
|
-
end
|
209
|
-
end
|
210
|
-
[server, subscopes]
|
197
|
+
[server, server.shards.merge(scope)]
|
211
198
|
end]
|
212
199
|
else
|
213
200
|
scopes = scope.group_by(&:database_server)
|
214
|
-
if parallel > 1
|
215
|
-
parallel = [(max_procs.to_f / scopes.count).ceil, parallel].min if max_procs
|
216
|
-
scopes = Hash[scopes.map do |(server, shards)|
|
217
|
-
[server, shards.in_groups(parallel, false).compact]
|
218
|
-
end]
|
219
|
-
else
|
220
|
-
scopes = Hash[scopes.map { |(server, shards)| [server, [shards]] }]
|
221
|
-
end
|
222
201
|
end
|
223
202
|
|
224
203
|
exception_pipes = []
|
@@ -244,8 +223,8 @@ module Switchman
|
|
244
223
|
end
|
245
224
|
|
246
225
|
# only one process; don't bother forking
|
247
|
-
if scopes.length == 1
|
248
|
-
return with_each_shard(scopes.first.last
|
226
|
+
if scopes.length == 1
|
227
|
+
return with_each_shard(scopes.first.last, categories, options) { yield }
|
249
228
|
end
|
250
229
|
|
251
230
|
# clear connections prior to forking (no more queries will be executed in the parent,
|
@@ -253,84 +232,78 @@ module Switchman
|
|
253
232
|
# silly like dealloc'ing prepared statements)
|
254
233
|
::ActiveRecord::Base.clear_all_connections!
|
255
234
|
|
256
|
-
scopes.each do |server,
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
235
|
+
scopes.each do |server, subscope|
|
236
|
+
name = server.id
|
237
|
+
|
238
|
+
exception_pipe = IO.pipe
|
239
|
+
exception_pipes << exception_pipe
|
240
|
+
pid, io_in, io_out, io_err = Open4.pfork4(lambda do
|
241
|
+
begin
|
242
|
+
Switchman.config[:on_fork_proc]&.call
|
243
|
+
|
244
|
+
# set a pretty name for the process title, up to 128 characters
|
245
|
+
# (we don't actually know the limit, depending on how the process
|
246
|
+
# was started)
|
247
|
+
# first, simplify the binary name by stripping directories,
|
248
|
+
# then truncate arguments as necessary
|
249
|
+
bin = File.basename($0) # Process.argv0 doesn't work on Ruby 2.5 (https://bugs.ruby-lang.org/issues/15887)
|
250
|
+
max_length = 128 - bin.length - name.length - 3
|
251
|
+
args = ARGV.join(" ")
|
252
|
+
if max_length >= 0
|
253
|
+
args = args[0..max_length]
|
254
|
+
end
|
255
|
+
new_title = [bin, args, name].join(" ")
|
256
|
+
Process.setproctitle(new_title)
|
263
257
|
|
264
|
-
|
265
|
-
|
266
|
-
|
258
|
+
with_each_shard(subscope, categories, options) { yield }
|
259
|
+
exception_pipe.last.close
|
260
|
+
rescue Exception => e
|
267
261
|
begin
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
# first, simplify the binary name by stripping directories,
|
274
|
-
# then truncate arguments as necessary
|
275
|
-
bin = File.basename($0) # Process.argv0 doesn't work on Ruby 2.5 (https://bugs.ruby-lang.org/issues/15887)
|
276
|
-
max_length = 128 - bin.length - name.length - 3
|
277
|
-
args = ARGV.join(" ")
|
278
|
-
if max_length >= 0
|
279
|
-
args = args[0..max_length]
|
280
|
-
end
|
281
|
-
new_title = [bin, args, name].join(" ")
|
282
|
-
Process.setproctitle(new_title)
|
283
|
-
|
284
|
-
with_each_shard(subscope, categories, options) { yield }
|
285
|
-
exception_pipe.last.close
|
286
|
-
rescue Exception => e
|
287
|
-
begin
|
288
|
-
dumped = Marshal.dump(e)
|
289
|
-
dumped = nil if dumped.length > 64 * 1024
|
290
|
-
rescue
|
291
|
-
dumped = nil
|
292
|
-
end
|
262
|
+
dumped = Marshal.dump(e)
|
263
|
+
dumped = nil if dumped.length > 64 * 1024
|
264
|
+
rescue
|
265
|
+
dumped = nil
|
266
|
+
end
|
293
267
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
end
|
303
|
-
e2.set_backtrace(backtrace)
|
304
|
-
e2.instance_variable_set(:@active_shards, e.instance_variable_get(:@active_shards))
|
305
|
-
dumped = Marshal.dump(e2)
|
268
|
+
if dumped.nil?
|
269
|
+
# couldn't dump the exception; create a copy with just
|
270
|
+
# the message and the backtrace
|
271
|
+
e2 = e.class.new(e.message)
|
272
|
+
backtrace = e.backtrace
|
273
|
+
# truncate excessively long backtraces
|
274
|
+
if backtrace.length > 50
|
275
|
+
backtrace = backtrace[0...25] + ['...'] + backtrace[-25..-1]
|
306
276
|
end
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
exception_pipe.last.flush
|
311
|
-
exception_pipe.last.close
|
312
|
-
exit! 1
|
277
|
+
e2.set_backtrace(backtrace)
|
278
|
+
e2.instance_variable_set(:@active_shards, e.instance_variable_get(:@active_shards))
|
279
|
+
dumped = Marshal.dump(e2)
|
313
280
|
end
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
281
|
+
|
282
|
+
exception_pipe.last.set_encoding(dumped.encoding)
|
283
|
+
exception_pipe.last.write(dumped)
|
284
|
+
exception_pipe.last.flush
|
285
|
+
exception_pipe.last.close
|
286
|
+
exit! 1
|
287
|
+
end
|
288
|
+
end)
|
289
|
+
exception_pipe.last.close
|
290
|
+
pids << pid
|
291
|
+
io_in.close # don't care about writing to stdin
|
292
|
+
out_fds << io_out
|
293
|
+
err_fds << io_err
|
294
|
+
pid_to_name_map[pid] = name
|
295
|
+
fd_to_name_map[io_out] = name
|
296
|
+
fd_to_name_map[io_err] = name
|
297
|
+
|
298
|
+
while pids.count >= parallel
|
299
|
+
while out_fds.count >= parallel
|
300
|
+
# wait for output if we've hit the parallel limit
|
301
|
+
wait_for_output.call(out_fds, err_fds, fd_to_name_map)
|
333
302
|
end
|
303
|
+
# we've gotten all the output from one fd so wait for its child process to exit
|
304
|
+
found_pid, status = Process.wait2
|
305
|
+
pids.delete(found_pid)
|
306
|
+
errors << pid_to_name_map[found_pid] if status.exitstatus != 0
|
334
307
|
end
|
335
308
|
end
|
336
309
|
|
@@ -544,24 +517,6 @@ module Switchman
|
|
544
517
|
shard || source_shard || Shard.current
|
545
518
|
end
|
546
519
|
|
547
|
-
# given the provided option, determines whether we need to (and whether
|
548
|
-
# it's possible) to determine a reasonable default.
|
549
|
-
def determine_max_procs(max_procs_input, parallel_input=2)
|
550
|
-
max_procs = nil
|
551
|
-
if max_procs_input
|
552
|
-
max_procs = max_procs_input.to_i
|
553
|
-
max_procs = nil if max_procs == 0
|
554
|
-
else
|
555
|
-
return 1 if parallel_input.nil? || parallel_input < 1
|
556
|
-
cpus = Environment.cpu_count
|
557
|
-
if cpus && cpus > 0
|
558
|
-
max_procs = cpus * parallel_input
|
559
|
-
end
|
560
|
-
end
|
561
|
-
|
562
|
-
return max_procs
|
563
|
-
end
|
564
|
-
|
565
520
|
private
|
566
521
|
# in-process caching
|
567
522
|
def cached_shards
|
@@ -38,11 +38,15 @@ module Switchman
|
|
38
38
|
def define_method_global_attribute(attr_name)
|
39
39
|
if sharded_column?(attr_name)
|
40
40
|
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
41
|
-
def
|
41
|
+
def __temp_global_attribute__
|
42
|
+
raw_value = original_#{attr_name}
|
43
|
+
return nil if raw_value.nil?
|
44
|
+
return raw_value if raw_value > Shard::IDS_PER_SHARD
|
45
|
+
|
42
46
|
Shard.global_id_for(original_#{attr_name}, shard)
|
43
47
|
end
|
44
|
-
alias_method 'global_#{attr_name}', :
|
45
|
-
undef_method :
|
48
|
+
alias_method 'global_#{attr_name}', :__temp_global_attribute__
|
49
|
+
undef_method :__temp_global_attribute__
|
46
50
|
RUBY
|
47
51
|
else
|
48
52
|
define_method_unsharded_column(attr_name, 'global')
|
@@ -52,11 +56,13 @@ module Switchman
|
|
52
56
|
def define_method_local_attribute(attr_name)
|
53
57
|
if sharded_column?(attr_name)
|
54
58
|
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
55
|
-
def
|
56
|
-
|
59
|
+
def __temp_local_attribute__
|
60
|
+
raw_value = original_#{attr_name}
|
61
|
+
return nil if raw_value.nil?
|
62
|
+
return raw_value % Shard::IDS_PER_SHARD
|
57
63
|
end
|
58
|
-
alias_method 'local_#{attr_name}', :
|
59
|
-
undef_method :
|
64
|
+
alias_method 'local_#{attr_name}', :__temp_local_attribute__
|
65
|
+
undef_method :__temp_local_attribute__
|
60
66
|
RUBY
|
61
67
|
else
|
62
68
|
define_method_unsharded_column(attr_name, 'local')
|
@@ -84,7 +90,7 @@ module Switchman
|
|
84
90
|
def define_method_original_attribute(attr_name)
|
85
91
|
if sharded_column?(attr_name)
|
86
92
|
reflection = reflection_for_integer_attribute(attr_name)
|
87
|
-
if attr_name == "id"
|
93
|
+
if attr_name == "id"
|
88
94
|
return if self.method_defined?(:original_id)
|
89
95
|
owner = self
|
90
96
|
else
|
@@ -94,18 +100,33 @@ module Switchman
|
|
94
100
|
# rename the original method to original_
|
95
101
|
alias_method 'original_#{attr_name}', '#{attr_name}'
|
96
102
|
# and replace with one that transposes the id
|
97
|
-
def
|
98
|
-
|
103
|
+
def __temp_relative_attribute__
|
104
|
+
raw_value = original_#{attr_name}
|
105
|
+
return nil if raw_value.nil?
|
106
|
+
|
107
|
+
abs_raw_value = raw_value.abs
|
108
|
+
current_shard = Shard.current(#{shard_category_code_for_reflection(reflection)})
|
109
|
+
same_shard = shard == current_shard
|
110
|
+
return raw_value if same_shard && abs_raw_value < Shard::IDS_PER_SHARD
|
111
|
+
|
112
|
+
value_shard_id = abs_raw_value / Shard::IDS_PER_SHARD
|
113
|
+
# this is a stupid case when someone stuffed a global id for the current shard in instead
|
114
|
+
# of a local id
|
115
|
+
return raw_value % Shard::IDS_PER_SHARD if value_shard_id == current_shard.id
|
116
|
+
return raw_value if !same_shard && abs_raw_value > Shard::IDS_PER_SHARD
|
117
|
+
return shard.global_id_for(raw_value) if !same_shard && abs_raw_value < Shard::IDS_PER_SHARD
|
118
|
+
|
119
|
+
Shard.relative_id_for(raw_value, shard, current_shard)
|
99
120
|
end
|
100
|
-
alias_method '#{attr_name}', :
|
101
|
-
undef_method :
|
121
|
+
alias_method '#{attr_name}', :__temp_relative_attribute__
|
122
|
+
undef_method :__temp_relative_attribute__
|
102
123
|
|
103
124
|
alias_method 'original_#{attr_name}=', '#{attr_name}='
|
104
|
-
def
|
125
|
+
def __temp_relative_attribute_assignment__(new_value)
|
105
126
|
self.original_#{attr_name} = Shard.relative_id_for(new_value, Shard.current(#{shard_category_code_for_reflection(reflection)}), shard)
|
106
127
|
end
|
107
|
-
alias_method '#{attr_name}=', :
|
108
|
-
undef_method :
|
128
|
+
alias_method '#{attr_name}=', :__temp_relative_attribute_assignment__
|
129
|
+
undef_method :__temp_relative_attribute_assignment__
|
109
130
|
RUBY
|
110
131
|
else
|
111
132
|
define_method_unsharded_column(attr_name, 'global')
|
@@ -115,11 +136,11 @@ module Switchman
|
|
115
136
|
def define_method_unsharded_column(attr_name, prefix)
|
116
137
|
return if columns_hash["#{prefix}_#{attr_name}"]
|
117
138
|
generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
118
|
-
def
|
139
|
+
def __temp_unsharded_attribute__
|
119
140
|
raise NoMethodError, "undefined method `#{prefix}_#{attr_name}'; are you missing an association?"
|
120
141
|
end
|
121
|
-
alias_method '#{prefix}_#{attr_name}', :
|
122
|
-
undef_method :
|
142
|
+
alias_method '#{prefix}_#{attr_name}', :__temp_unsharded_attribute__
|
143
|
+
undef_method :__temp_unsharded_attribute__
|
123
144
|
RUBY
|
124
145
|
end
|
125
146
|
end
|
@@ -80,17 +80,17 @@ module Switchman
|
|
80
80
|
end
|
81
81
|
end
|
82
82
|
|
83
|
-
def self.
|
84
|
-
klass.
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
end
|
83
|
+
def self.prepended(klass)
|
84
|
+
klass.singleton_class.prepend(ClassMethods)
|
85
|
+
end
|
86
|
+
|
87
|
+
def _run_initialize_callbacks
|
88
|
+
@shard ||= if self.class.sharded_primary_key?
|
89
|
+
Shard.shard_for(self[self.class.primary_key], Shard.current(self.class.shard_category))
|
90
|
+
else
|
91
|
+
Shard.current(self.class.shard_category)
|
93
92
|
end
|
93
|
+
super
|
94
94
|
end
|
95
95
|
|
96
96
|
def shard
|
@@ -158,7 +158,7 @@ module Switchman
|
|
158
158
|
end
|
159
159
|
|
160
160
|
def update_columns(*)
|
161
|
-
db =
|
161
|
+
db = shard.database_server
|
162
162
|
if ::GuardRail.environment != db.guard_rail_environment
|
163
163
|
return db.unguard { super }
|
164
164
|
else
|
@@ -118,7 +118,7 @@ module Switchman
|
|
118
118
|
next if k == spec_name
|
119
119
|
|
120
120
|
v = owner_to_pool[k]
|
121
|
-
owner_to_pool.delete(k) if v.is_a?(ConnectionPoolProxy) && v.spec.name == spec_name
|
121
|
+
owner_to_pool.delete(k) if v.is_a?(ConnectionPoolProxy) && v.default_pool.spec.name == spec_name
|
122
122
|
end
|
123
123
|
|
124
124
|
# unwrap the pool from inside a ConnectionPoolProxy
|
@@ -20,10 +20,7 @@ module Switchman
|
|
20
20
|
shard = " [#{shard[:database_server_id]}:#{shard[:id]} #{shard[:env]}]" if shard
|
21
21
|
|
22
22
|
unless (payload[:binds] || []).empty?
|
23
|
-
|
24
|
-
args = use_old_format ?
|
25
|
-
[payload[:binds], payload[:type_casted_binds]] :
|
26
|
-
[payload[:type_casted_binds]]
|
23
|
+
args = [payload[:type_casted_binds]]
|
27
24
|
casted_params = type_casted_binds(*args)
|
28
25
|
binds = " " + payload[:binds].zip(casted_params).map { |attr, value|
|
29
26
|
render_bind(attr, value)
|
@@ -30,10 +30,13 @@ module Switchman
|
|
30
30
|
end
|
31
31
|
|
32
32
|
module Migrator
|
33
|
-
# significant change:
|
33
|
+
# significant change: just return MIGRATOR_SALT directly
|
34
|
+
# especially if you're going through pgbouncer, the database
|
35
|
+
# name you're accessing may not be consistent. it is NOT allowed
|
36
|
+
# to run migrations against multiple shards in the same database
|
37
|
+
# concurrently
|
34
38
|
def generate_migrator_advisory_lock_id
|
35
|
-
|
36
|
-
::ActiveRecord::Migrator::MIGRATOR_SALT * shard_name_hash
|
39
|
+
::ActiveRecord::Migrator::MIGRATOR_SALT
|
37
40
|
end
|
38
41
|
|
39
42
|
if ::Rails.version >= '6.0'
|
@@ -13,6 +13,15 @@ module Switchman
|
|
13
13
|
shard.activate(self.class.shard_category) { super }
|
14
14
|
end
|
15
15
|
end
|
16
|
+
|
17
|
+
def delete
|
18
|
+
db = shard.database_server
|
19
|
+
if ::GuardRail.environment != db.guard_rail_environment
|
20
|
+
return db.unguard { super }
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
16
25
|
end
|
17
26
|
end
|
18
|
-
end
|
27
|
+
end
|
@@ -102,7 +102,7 @@ module Switchman
|
|
102
102
|
WHERE i.relkind = 'i'
|
103
103
|
AND d.indisprimary = 'f'
|
104
104
|
AND t.relname = '#{table_name}'
|
105
|
-
AND
|
105
|
+
AND t.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = '#{shard.name}' )
|
106
106
|
ORDER BY i.relname
|
107
107
|
SQL
|
108
108
|
|
@@ -19,7 +19,7 @@ module Switchman
|
|
19
19
|
connection_id: object_id,
|
20
20
|
cached: true
|
21
21
|
}
|
22
|
-
args[:type_casted_binds] = -> { type_casted_binds(binds) }
|
22
|
+
args[:type_casted_binds] = -> { type_casted_binds(binds) }
|
23
23
|
::ActiveSupport::Notifications.instrument(
|
24
24
|
"sql.active_record",
|
25
25
|
args
|
@@ -78,6 +78,10 @@ module Switchman
|
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
|
+
def or(other)
|
82
|
+
super(other.shard(self.primary_shard))
|
83
|
+
end
|
84
|
+
|
81
85
|
private
|
82
86
|
|
83
87
|
if ::Rails.version >= '5.2'
|
@@ -258,6 +262,25 @@ module Switchman
|
|
258
262
|
end)
|
259
263
|
end
|
260
264
|
|
265
|
+
if predicate.is_a?(::Arel::Nodes::Grouping)
|
266
|
+
next predicate unless predicate.expr.is_a?(::Arel::Nodes::Or)
|
267
|
+
|
268
|
+
or_expr = predicate.expr
|
269
|
+
left_node = or_expr.left
|
270
|
+
right_node = or_expr.right
|
271
|
+
new_left_predicates, binds = transpose_predicates([left_node], source_shard, target_shard,
|
272
|
+
remove_nonlocal_primary_keys,
|
273
|
+
binds: binds,
|
274
|
+
dup_binds_on_mutation: dup_binds_on_mutation)
|
275
|
+
new_right_predicates, binds = transpose_predicates([right_node], source_shard, target_shard,
|
276
|
+
remove_nonlocal_primary_keys,
|
277
|
+
binds: binds,
|
278
|
+
dup_binds_on_mutation: dup_binds_on_mutation)
|
279
|
+
|
280
|
+
next predicate if new_left_predicates[0] == left_node && new_right_predicates[0] == right_node
|
281
|
+
next ::Arel::Nodes::Grouping.new ::Arel::Nodes::Or.new(new_left_predicates[0], new_right_predicates[0])
|
282
|
+
end
|
283
|
+
|
261
284
|
next predicate unless predicate.is_a?(::Arel::Nodes::Binary)
|
262
285
|
next predicate unless predicate.left.is_a?(::Arel::Attributes::Attribute)
|
263
286
|
relation, column = relation_and_column(predicate.left)
|
data/lib/switchman/engine.rb
CHANGED
@@ -95,7 +95,7 @@ module Switchman
|
|
95
95
|
|
96
96
|
::StandardError.include(StandardError)
|
97
97
|
|
98
|
-
|
98
|
+
prepend ActiveRecord::Base
|
99
99
|
include ActiveRecord::AttributeMethods
|
100
100
|
include ActiveRecord::Persistence
|
101
101
|
singleton_class.prepend ActiveRecord::ModelSchema::ClassMethods
|
data/lib/switchman/version.rb
CHANGED
data/lib/tasks/switchman.rake
CHANGED
@@ -46,7 +46,8 @@ module Switchman
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def self.options
|
49
|
-
|
49
|
+
# we still pass through both of these options for back-compat purposes
|
50
|
+
{ parallel: ENV['PARALLEL']&.to_i, max_procs: ENV['MAX_PARALLEL_PROCS']&.to_i }
|
50
51
|
end
|
51
52
|
|
52
53
|
# categories - an array or proc, to activate as the current shard during the
|
@@ -89,7 +90,7 @@ module Switchman
|
|
89
90
|
nil
|
90
91
|
end
|
91
92
|
rescue => e
|
92
|
-
puts "Exception from #{e.current_shard.id}: #{e.current_shard.description}" if options[:parallel] != 0
|
93
|
+
puts "Exception from #{e.current_shard.id}: #{e.current_shard.description}" if options[:parallel].to_i != 0
|
93
94
|
raise
|
94
95
|
end
|
95
96
|
end
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: switchman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cody Cutrer
|
8
8
|
- James Williams
|
9
9
|
- Jacob Fugal
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2022-03-09 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: railties
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: '5.
|
21
|
+
version: '5.2'
|
22
22
|
- - "<"
|
23
23
|
- !ruby/object:Gem::Version
|
24
24
|
version: '6.1'
|
@@ -28,7 +28,7 @@ dependencies:
|
|
28
28
|
requirements:
|
29
29
|
- - ">="
|
30
30
|
- !ruby/object:Gem::Version
|
31
|
-
version: '5.
|
31
|
+
version: '5.2'
|
32
32
|
- - "<"
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '6.1'
|
@@ -38,7 +38,7 @@ dependencies:
|
|
38
38
|
requirements:
|
39
39
|
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '5.
|
41
|
+
version: '5.2'
|
42
42
|
- - "<"
|
43
43
|
- !ruby/object:Gem::Version
|
44
44
|
version: '6.1'
|
@@ -48,7 +48,7 @@ dependencies:
|
|
48
48
|
requirements:
|
49
49
|
- - ">="
|
50
50
|
- !ruby/object:Gem::Version
|
51
|
-
version: '5.
|
51
|
+
version: '5.2'
|
52
52
|
- - "<"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '6.1'
|
@@ -257,7 +257,7 @@ homepage: http://www.instructure.com/
|
|
257
257
|
licenses:
|
258
258
|
- MIT
|
259
259
|
metadata: {}
|
260
|
-
post_install_message:
|
260
|
+
post_install_message:
|
261
261
|
rdoc_options: []
|
262
262
|
require_paths:
|
263
263
|
- lib
|
@@ -265,15 +265,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
265
265
|
requirements:
|
266
266
|
- - ">="
|
267
267
|
- !ruby/object:Gem::Version
|
268
|
-
version: '2.
|
268
|
+
version: '2.6'
|
269
269
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
270
270
|
requirements:
|
271
271
|
- - ">="
|
272
272
|
- !ruby/object:Gem::Version
|
273
273
|
version: '0'
|
274
274
|
requirements: []
|
275
|
-
rubygems_version: 3.
|
276
|
-
signing_key:
|
275
|
+
rubygems_version: 3.1.4
|
276
|
+
signing_key:
|
277
277
|
specification_version: 4
|
278
278
|
summary: Rails sharding magic
|
279
279
|
test_files: []
|