switchman 3.2.1 → 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []
|