switchman 3.2.1 → 3.3.1
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d0330d210ee4f44c8ceb3b66ba1536f5538c4a1efcd4a939d492ffa65602fca7
|
4
|
+
data.tar.gz: '0787f10b30acc34e0a5e48423c524d2db27b8b92dabd76bf567fb6631c0587c3'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 65e0cbb8c9015940209145aa229648fa763d999399053e9046b0b4a60e1aae1ebfe419983776364e7e2460ca2e0cc0021188d286a8da6a9d9c0a74bd59d1bbc2
|
7
|
+
data.tar.gz: db938a240af2ffb146453536d42db25d38ac13ca136b757d219fef46cf99476c0916beb65c8e06e2bc48067b94736d8f88b4c13519f85d99ec2b07fc423eccc9
|
@@ -12,8 +12,6 @@ module Switchman
|
|
12
12
|
# :explicit - explicit set on the relation
|
13
13
|
# :association - a special value that scopes from associations use to use slightly different logic
|
14
14
|
# for foreign key transposition
|
15
|
-
# :to_a - a special value that Relation#to_a uses when querying multiple shards to
|
16
|
-
# remove primary keys from conditions that aren't applicable to the current shard
|
17
15
|
def shard_value
|
18
16
|
@values[:shard]
|
19
17
|
end
|
@@ -44,10 +42,7 @@ module Switchman
|
|
44
42
|
old_primary_shard = primary_shard
|
45
43
|
self.shard_value = value
|
46
44
|
self.shard_source_value = source
|
47
|
-
|
48
|
-
transpose_clauses(old_primary_shard, primary_shard,
|
49
|
-
remove_nonlocal_primary_keys: source == :to_a)
|
50
|
-
end
|
45
|
+
transpose_predicates(nil, old_primary_shard, primary_shard) if old_primary_shard != primary_shard
|
51
46
|
self
|
52
47
|
end
|
53
48
|
|
@@ -89,29 +84,21 @@ module Switchman
|
|
89
84
|
super(other.shard(primary_shard))
|
90
85
|
end
|
91
86
|
|
92
|
-
|
87
|
+
protected
|
93
88
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
if new_predicates != predicates
|
103
|
-
#{type}_clause.instance_variable_set(:@predicates, new_predicates)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
89
|
+
def remove_nonlocal_primary_keys!
|
90
|
+
each_transposable_predicate_value do |value, predicate, _relation, _column, type|
|
91
|
+
next value unless
|
92
|
+
type == :primary &&
|
93
|
+
predicate.left.relation.klass == klass &&
|
94
|
+
(predicate.is_a?(::Arel::Nodes::Equality) || predicate.is_a?(::Arel::Nodes::HomogeneousIn))
|
95
|
+
|
96
|
+
value.is_a?(Integer) && value > Shard::IDS_PER_SHARD ? [] : value
|
107
97
|
end
|
108
|
-
|
98
|
+
self
|
109
99
|
end
|
110
100
|
|
111
|
-
|
112
|
-
transpose_where_clauses(source_shard, target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
113
|
-
transpose_having_clauses(source_shard, target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
114
|
-
end
|
101
|
+
private
|
115
102
|
|
116
103
|
def infer_shards_from_primary_key(predicates)
|
117
104
|
return unless klass.integral_id?
|
@@ -145,7 +132,7 @@ module Switchman
|
|
145
132
|
return
|
146
133
|
else
|
147
134
|
id_shards = id_shards.to_a
|
148
|
-
|
135
|
+
transpose_predicates(nil, primary_shard, id_shards.first)
|
149
136
|
self.shard_value = id_shards
|
150
137
|
return
|
151
138
|
end
|
@@ -162,7 +149,7 @@ module Switchman
|
|
162
149
|
|
163
150
|
return if !id_shard || id_shard == primary_shard
|
164
151
|
|
165
|
-
|
152
|
+
transpose_predicates(nil, primary_shard, id_shard)
|
166
153
|
self.shard_value = id_shard
|
167
154
|
end
|
168
155
|
|
@@ -246,122 +233,129 @@ module Switchman
|
|
246
233
|
connection.with_global_table_name { super }
|
247
234
|
end
|
248
235
|
|
249
|
-
def
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
transpose_single_predicate(predicate, source_shard, target_shard,
|
255
|
-
remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
256
|
-
end
|
236
|
+
def each_predicate(predicates = nil, &block)
|
237
|
+
return predicates.map(&block) if predicates
|
238
|
+
|
239
|
+
each_predicate_cb(:having_clause, :having_clause=, &block)
|
240
|
+
each_predicate_cb(:where_clause, :where_clause=, &block)
|
257
241
|
end
|
258
242
|
|
259
|
-
def
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
if predicate.is_a?(::Arel::Nodes::Grouping)
|
264
|
-
return predicate unless predicate.expr.is_a?(::Arel::Nodes::Or)
|
265
|
-
|
266
|
-
# Dang, we have an OR. OK, that means we have other epxressions below this
|
267
|
-
# level, perhaps many, that may need transposition.
|
268
|
-
# the left side and right side must each be treated as predicate lists and
|
269
|
-
# transformed in kind, if neither of them changes we can just return the grouping as is.
|
270
|
-
# hold on, it's about to get recursive...
|
271
|
-
or_expr = predicate.expr
|
272
|
-
left_node = or_expr.left
|
273
|
-
right_node = or_expr.right
|
274
|
-
new_left_predicates = transpose_single_predicate(left_node, source_shard,
|
275
|
-
target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
276
|
-
new_right_predicates = transpose_single_predicate(right_node, source_shard,
|
277
|
-
target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
278
|
-
return predicate if new_left_predicates == left_node && new_right_predicates == right_node
|
279
|
-
|
280
|
-
return ::Arel::Nodes::Grouping.new ::Arel::Nodes::Or.new(new_left_predicates, new_right_predicates)
|
281
|
-
end
|
282
|
-
return predicate unless predicate.is_a?(::Arel::Nodes::Binary) || predicate.is_a?(::Arel::Nodes::HomogeneousIn)
|
283
|
-
return predicate unless predicate.left.is_a?(::Arel::Attributes::Attribute)
|
284
|
-
|
285
|
-
relation, column = relation_and_column(predicate.left)
|
286
|
-
return predicate unless (type = transposable_attribute_type(relation, column))
|
287
|
-
|
288
|
-
remove = true if type == :primary &&
|
289
|
-
remove_nonlocal_primary_keys &&
|
290
|
-
predicate.left.relation.klass == klass &&
|
291
|
-
(predicate.is_a?(::Arel::Nodes::Equality) || predicate.is_a?(::Arel::Nodes::HomogeneousIn))
|
292
|
-
|
293
|
-
current_source_shard =
|
294
|
-
if source_shard
|
295
|
-
source_shard
|
296
|
-
elsif type == :primary
|
297
|
-
Shard.current(klass.connection_class_for_self)
|
298
|
-
elsif type == :foreign
|
299
|
-
source_shard_for_foreign_key(relation, column)
|
300
|
-
end
|
243
|
+
def each_predicate_cb(clause_getter, clause_setter, &block)
|
244
|
+
old_clause = send(clause_getter)
|
245
|
+
old_predicates = old_clause.send(:predicates)
|
246
|
+
return if old_predicates.empty?
|
301
247
|
|
302
|
-
|
303
|
-
|
304
|
-
else
|
305
|
-
predicate.right
|
306
|
-
end
|
248
|
+
new_predicates = old_predicates.map(&block)
|
249
|
+
return if new_predicates == old_predicates
|
307
250
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
251
|
+
new_clause = old_clause.dup
|
252
|
+
new_clause.instance_variable_set(:@predicates, new_predicates)
|
253
|
+
|
254
|
+
send(clause_setter, new_clause)
|
255
|
+
end
|
256
|
+
|
257
|
+
def each_transposable_predicate(predicates = nil, &block)
|
258
|
+
each_predicate(predicates) do |predicate|
|
259
|
+
if predicate.is_a?(::Arel::Nodes::Grouping)
|
260
|
+
next predicate unless predicate.expr.is_a?(::Arel::Nodes::Or)
|
261
|
+
|
262
|
+
or_expr = predicate.expr
|
263
|
+
old_left = or_expr.left
|
264
|
+
old_right = or_expr.right
|
265
|
+
new_left, new_right = each_transposable_predicate([old_left, old_right], &block)
|
266
|
+
|
267
|
+
next predicate if new_left == old_left && new_right == old_right
|
268
|
+
|
269
|
+
next predicate.class.new predicate.expr.class.new(new_left, new_right)
|
314
270
|
end
|
315
271
|
|
316
|
-
|
317
|
-
predicate
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
272
|
+
next predicate unless predicate.is_a?(::Arel::Nodes::Binary) || predicate.is_a?(::Arel::Nodes::HomogeneousIn)
|
273
|
+
next predicate unless predicate.left.is_a?(::Arel::Attributes::Attribute)
|
274
|
+
|
275
|
+
relation, column = relation_and_column(predicate.left)
|
276
|
+
next predicate unless (type = transposable_attribute_type(relation, column))
|
277
|
+
|
278
|
+
yield(predicate, relation, column, type)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def each_transposable_predicate_value(predicates = nil)
|
283
|
+
each_transposable_predicate(predicates) do |predicate, relation, column, type|
|
284
|
+
each_transposable_predicate_value_cb(predicate) do |value|
|
285
|
+
yield(value, predicate, relation, column, type)
|
323
286
|
end
|
324
|
-
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def each_transposable_predicate_value_cb(node, &block)
|
291
|
+
case node
|
292
|
+
when Array
|
293
|
+
node.map { |val| each_transposable_predicate_value_cb(val, &block).presence }.compact
|
294
|
+
when ::ActiveModel::Attribute
|
295
|
+
old_value = node.value_before_type_cast
|
296
|
+
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
297
|
+
|
298
|
+
old_value == new_value ? node : node.class.new(node.name, new_value, node.type)
|
299
|
+
when ::Arel::Nodes::And
|
300
|
+
old_value = node.children
|
301
|
+
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
302
|
+
|
303
|
+
old_value == new_value ? node : node.class.new(new_value)
|
304
|
+
when ::Arel::Nodes::BindParam
|
305
|
+
old_value = node.value
|
306
|
+
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
307
|
+
|
308
|
+
old_value == new_value ? node : node.class.new(new_value)
|
309
|
+
when ::Arel::Nodes::Casted
|
310
|
+
old_value = node.value
|
311
|
+
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
312
|
+
|
313
|
+
old_value == new_value ? node : node.class.new(new_value, node.attribute)
|
314
|
+
when ::Arel::Nodes::HomogeneousIn
|
315
|
+
old_value = node.values
|
316
|
+
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
317
|
+
|
325
318
|
# switch to a regular In, so that Relation::WhereClause#contradiction? knows about it
|
326
|
-
if
|
327
|
-
klass =
|
328
|
-
klass.new(
|
319
|
+
if new_value.empty?
|
320
|
+
klass = node.type == :in ? ::Arel::Nodes::In : ::Arel::Nodes::NotIn
|
321
|
+
klass.new(node.attribute, new_value)
|
329
322
|
else
|
330
|
-
|
323
|
+
old_value == new_value ? node : node.class.new(new_value, node.attribute, node.type)
|
331
324
|
end
|
325
|
+
when ::Arel::Nodes::Binary
|
326
|
+
old_value = node.right
|
327
|
+
new_value = each_transposable_predicate_value_cb(old_value, &block)
|
328
|
+
|
329
|
+
old_value == new_value ? node : node.class.new(node.left, new_value)
|
332
330
|
else
|
333
|
-
|
331
|
+
yield(node)
|
334
332
|
end
|
335
333
|
end
|
336
334
|
|
337
|
-
def
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
local_id = [] if remove_non_local_ids && local_id.is_a?(Integer) && local_id > Shard::IDS_PER_SHARD
|
349
|
-
if current_id == local_id
|
350
|
-
# make a new bind param
|
351
|
-
value
|
352
|
-
else
|
353
|
-
new_att = query_att.class.new(query_att.name, local_id, query_att.type)
|
354
|
-
if value.is_a?(::ActiveModel::Attribute)
|
355
|
-
new_att
|
356
|
-
else
|
357
|
-
::Arel::Nodes::BindParam.new(new_att)
|
358
|
-
end
|
335
|
+
def transpose_predicates(predicates,
|
336
|
+
source_shard,
|
337
|
+
target_shard)
|
338
|
+
each_transposable_predicate_value(predicates) do |value, _predicate, relation, column, type|
|
339
|
+
current_source_shard =
|
340
|
+
if source_shard
|
341
|
+
source_shard
|
342
|
+
elsif type == :primary
|
343
|
+
Shard.current(klass.connection_class_for_self)
|
344
|
+
elsif type == :foreign
|
345
|
+
source_shard_for_foreign_key(relation, column)
|
359
346
|
end
|
360
|
-
|
347
|
+
|
348
|
+
transpose_predicate_value(value, current_source_shard, target_shard, type)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
def transpose_predicate_value(value, current_shard, target_shard, attribute_type)
|
353
|
+
if value.is_a?(::ActiveRecord::StatementCache::Substitute)
|
354
|
+
value.sharded = true # mark for transposition later
|
355
|
+
value.primary = true if attribute_type == :primary
|
356
|
+
value
|
361
357
|
else
|
362
|
-
|
363
|
-
local_id = [] if remove_non_local_ids && local_id.is_a?(Integer) && local_id > Shard::IDS_PER_SHARD
|
364
|
-
local_id
|
358
|
+
Shard.relative_id_for(value, current_shard, target_shard) || value
|
365
359
|
end
|
366
360
|
end
|
367
361
|
end
|
@@ -58,9 +58,10 @@ module Switchman
|
|
58
58
|
end
|
59
59
|
|
60
60
|
%I[update_all delete_all].each do |method|
|
61
|
+
arg_params = RUBY_VERSION <= '2.8' ? '*args' : '*args, **kwargs'
|
61
62
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
62
|
-
def #{method}(
|
63
|
-
result = self.activate(unordered: true) { |relation| relation.call_super(#{method.inspect}, Relation,
|
63
|
+
def #{method}(#{arg_params})
|
64
|
+
result = self.activate(unordered: true) { |relation| relation.call_super(#{method.inspect}, Relation, #{arg_params}) }
|
64
65
|
result = result.sum if result.is_a?(Array)
|
65
66
|
result
|
66
67
|
end
|
@@ -103,7 +104,9 @@ module Switchman
|
|
103
104
|
def activate(unordered: false, &block)
|
104
105
|
shards = all_shards
|
105
106
|
if Array === shards && shards.length == 1
|
106
|
-
if
|
107
|
+
if !loaded? && shard_value != shards.first
|
108
|
+
shard(shards.first).activate(&block)
|
109
|
+
elsif shards.first == DefaultShard || shards.first == Shard.current(klass.connection_class_for_self)
|
107
110
|
yield(self, shards.first)
|
108
111
|
else
|
109
112
|
shards.first.activate(klass.connection_class_for_self) { yield(self, shards.first) }
|
@@ -115,7 +118,8 @@ module Switchman
|
|
115
118
|
# don't even query other shards if we're already past the limit
|
116
119
|
next if limit_value && result_count >= limit_value && order_values.empty?
|
117
120
|
|
118
|
-
relation = shard(Shard.current(klass.connection_class_for_self)
|
121
|
+
relation = shard(Shard.current(klass.connection_class_for_self))
|
122
|
+
relation.remove_nonlocal_primary_keys!
|
119
123
|
# do a minimal query if possible
|
120
124
|
relation = relation.limit(limit_value - result_count) if limit_value && !result_count.zero? && order_values.empty?
|
121
125
|
|
data/lib/switchman/call_super.rb
CHANGED
@@ -12,8 +12,14 @@ module Switchman
|
|
12
12
|
method.super_method
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
if RUBY_VERSION <= '2.8'
|
16
|
+
def call_super(method, above_module, *args, &block)
|
17
|
+
super_method_above(method, above_module).call(*args, &block)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
def call_super(method, above_module, *args, **kwargs, &block)
|
21
|
+
super_method_above(method, above_module).call(*args, **kwargs, &block)
|
22
|
+
end
|
17
23
|
end
|
18
24
|
end
|
19
25
|
end
|
@@ -13,8 +13,9 @@ module Switchman
|
|
13
13
|
end
|
14
14
|
|
15
15
|
%w[update_all delete_all].each do |method|
|
16
|
+
arg_params = RUBY_VERSION <= '2.8' ? '*args' : '*args, **kwargs'
|
16
17
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
17
|
-
def #{method}(
|
18
|
+
def #{method}(#{arg_params})
|
18
19
|
db = Shard.current(connection_class_for_self).database_server
|
19
20
|
db.unguard { super }
|
20
21
|
end
|
data/lib/switchman/version.rb
CHANGED
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: 3.
|
4
|
+
version: 3.3.1
|
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: 2022-
|
13
|
+
date: 2022-12-20 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
@@ -300,7 +300,7 @@ licenses:
|
|
300
300
|
- MIT
|
301
301
|
metadata:
|
302
302
|
rubygems_mfa_required: 'true'
|
303
|
-
post_install_message:
|
303
|
+
post_install_message:
|
304
304
|
rdoc_options: []
|
305
305
|
require_paths:
|
306
306
|
- lib
|
@@ -315,8 +315,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
315
315
|
- !ruby/object:Gem::Version
|
316
316
|
version: '0'
|
317
317
|
requirements: []
|
318
|
-
rubygems_version: 3.
|
319
|
-
signing_key:
|
318
|
+
rubygems_version: 3.3.7
|
319
|
+
signing_key:
|
320
320
|
specification_version: 4
|
321
321
|
summary: Rails sharding magic
|
322
322
|
test_files: []
|