switchman 2.1.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +10 -2
- data/app/models/switchman/shard.rb +234 -282
- data/app/models/switchman/unsharded_record.rb +7 -0
- data/db/migrate/20130328212039_create_switchman_shards.rb +1 -1
- data/db/migrate/20130328224244_create_default_shard.rb +5 -5
- data/db/migrate/20161206323434_add_back_default_string_limits_switchman.rb +1 -0
- data/db/migrate/20180828183945_add_default_shard_index.rb +2 -2
- data/db/migrate/20180828192111_add_timestamps_to_shards.rb +7 -5
- data/db/migrate/20190114212900_add_unique_name_indexes.rb +5 -3
- data/lib/switchman/action_controller/caching.rb +2 -2
- data/lib/switchman/active_record/abstract_adapter.rb +1 -0
- data/lib/switchman/active_record/association.rb +78 -89
- data/lib/switchman/active_record/attribute_methods.rb +58 -52
- data/lib/switchman/active_record/base.rb +58 -59
- data/lib/switchman/active_record/calculations.rb +74 -67
- data/lib/switchman/active_record/connection_pool.rb +14 -41
- data/lib/switchman/active_record/database_configurations/database_config.rb +13 -0
- data/lib/switchman/active_record/database_configurations.rb +34 -0
- data/lib/switchman/active_record/finder_methods.rb +11 -16
- data/lib/switchman/active_record/log_subscriber.rb +4 -8
- data/lib/switchman/active_record/migration.rb +6 -47
- data/lib/switchman/active_record/model_schema.rb +1 -1
- data/lib/switchman/active_record/persistence.rb +4 -6
- data/lib/switchman/active_record/postgresql_adapter.rb +124 -168
- data/lib/switchman/active_record/predicate_builder.rb +2 -2
- data/lib/switchman/active_record/query_cache.rb +18 -19
- data/lib/switchman/active_record/query_methods.rb +172 -197
- data/lib/switchman/active_record/reflection.rb +7 -22
- data/lib/switchman/active_record/relation.rb +30 -78
- data/lib/switchman/active_record/spawn_methods.rb +27 -29
- data/lib/switchman/active_record/statement_cache.rb +18 -35
- data/lib/switchman/active_record/tasks/database_tasks.rb +16 -0
- data/lib/switchman/active_support/cache.rb +3 -5
- data/lib/switchman/arel.rb +13 -8
- data/lib/switchman/database_server.rb +121 -142
- data/lib/switchman/default_shard.rb +52 -16
- data/lib/switchman/engine.rb +61 -58
- data/lib/switchman/environment.rb +4 -8
- data/lib/switchman/errors.rb +1 -0
- data/lib/switchman/guard_rail/relation.rb +5 -7
- data/lib/switchman/guard_rail.rb +6 -19
- data/lib/switchman/r_spec_helper.rb +29 -37
- data/lib/switchman/rails.rb +14 -12
- data/lib/switchman/schema_cache.rb +1 -9
- data/lib/switchman/sharded_instrumenter.rb +1 -1
- data/lib/switchman/standard_error.rb +15 -3
- data/lib/switchman/test_helper.rb +6 -4
- data/lib/switchman/version.rb +1 -1
- data/lib/switchman.rb +3 -5
- data/lib/tasks/switchman.rake +54 -69
- metadata +87 -45
- data/lib/switchman/active_record/batches.rb +0 -11
- data/lib/switchman/active_record/connection_handler.rb +0 -190
- data/lib/switchman/active_record/where_clause_factory.rb +0 -36
- data/lib/switchman/connection_pool_proxy.rb +0 -173
@@ -17,15 +17,20 @@ module Switchman
|
|
17
17
|
def shard_value
|
18
18
|
@values[:shard]
|
19
19
|
end
|
20
|
+
|
20
21
|
def shard_source_value
|
21
22
|
@values[:shard_source]
|
22
23
|
end
|
24
|
+
|
23
25
|
def shard_value=(value)
|
24
26
|
raise ::ActiveRecord::ImmutableRelation if @loaded
|
27
|
+
|
25
28
|
@values[:shard] = value
|
26
29
|
end
|
30
|
+
|
27
31
|
def shard_source_value=(value)
|
28
32
|
raise ::ActiveRecord::ImmutableRelation if @loaded
|
33
|
+
|
29
34
|
@values[:shard_source] = value
|
30
35
|
end
|
31
36
|
|
@@ -35,11 +40,13 @@ module Switchman
|
|
35
40
|
|
36
41
|
def shard!(value, source = :explicit)
|
37
42
|
raise ArgumentError, "shard can't be nil" unless value
|
38
|
-
|
43
|
+
|
44
|
+
old_primary_shard = primary_shard
|
39
45
|
self.shard_value = value
|
40
46
|
self.shard_source_value = source
|
41
|
-
if
|
42
|
-
transpose_clauses(old_primary_shard,
|
47
|
+
if old_primary_shard != primary_shard || source == :to_a
|
48
|
+
transpose_clauses(old_primary_shard, primary_shard,
|
49
|
+
remove_nonlocal_primary_keys: source == :to_a)
|
43
50
|
end
|
44
51
|
self
|
45
52
|
end
|
@@ -58,7 +65,7 @@ module Switchman
|
|
58
65
|
when ::ActiveRecord::Relation
|
59
66
|
Shard.default
|
60
67
|
when nil
|
61
|
-
Shard.current(klass.
|
68
|
+
Shard.current(klass.connection_classes)
|
62
69
|
else
|
63
70
|
raise ArgumentError, "invalid shard value #{shard_value}"
|
64
71
|
end
|
@@ -72,114 +79,88 @@ module Switchman
|
|
72
79
|
when ::ActiveRecord::Base
|
73
80
|
shard_value.respond_to?(:associated_shards) ? shard_value.associated_shards : [shard_value.shard]
|
74
81
|
when nil
|
75
|
-
[Shard.current(klass.
|
82
|
+
[Shard.current(klass.connection_classes)]
|
76
83
|
else
|
77
84
|
shard_value
|
78
85
|
end
|
79
86
|
end
|
80
87
|
|
88
|
+
def or(other)
|
89
|
+
super(other.shard(primary_shard))
|
90
|
+
end
|
91
|
+
|
81
92
|
private
|
82
93
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
94
|
+
%i[where having].each do |type|
|
95
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
96
|
+
def transpose_#{type}_clauses(source_shard, target_shard, remove_nonlocal_primary_keys:)
|
97
|
+
unless (predicates = #{type}_clause.send(:predicates)).empty?
|
98
|
+
new_predicates = transpose_predicates(predicates, source_shard,
|
99
|
+
target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
100
|
+
if new_predicates != predicates
|
101
|
+
self.#{type}_clause = #{type}_clause.dup
|
90
102
|
if new_predicates != predicates
|
91
|
-
|
92
|
-
if new_predicates != predicates
|
93
|
-
#{type}_clause.instance_variable_set(:@predicates, new_predicates)
|
94
|
-
end
|
103
|
+
#{type}_clause.instance_variable_set(:@predicates, new_predicates)
|
95
104
|
end
|
96
105
|
end
|
97
106
|
end
|
98
|
-
RUBY
|
99
|
-
end
|
100
|
-
else
|
101
|
-
[:where, :having].each do |type|
|
102
|
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
103
|
-
def transpose_#{type}_clauses(source_shard, target_shard, remove_nonlocal_primary_keys)
|
104
|
-
unless (predicates = #{type}_clause.send(:predicates)).empty?
|
105
|
-
new_predicates, new_binds = transpose_predicates(predicates, source_shard,
|
106
|
-
target_shard, remove_nonlocal_primary_keys,
|
107
|
-
binds: #{type}_clause.binds,
|
108
|
-
dup_binds_on_mutation: true)
|
109
|
-
if new_predicates != predicates || !new_binds.equal?(#{type}_clause.binds)
|
110
|
-
self.#{type}_clause = #{type}_clause.dup
|
111
|
-
if new_predicates != predicates
|
112
|
-
#{type}_clause.instance_variable_set(:@predicates, new_predicates)
|
113
|
-
end
|
114
|
-
if !new_binds.equal?(#{type}_clause.binds)
|
115
|
-
#{type}_clause.instance_variable_set(:@binds, new_binds)
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
RUBY
|
121
107
|
end
|
108
|
+
RUBY
|
122
109
|
end
|
123
110
|
|
124
|
-
def transpose_clauses(source_shard, target_shard, remove_nonlocal_primary_keys
|
125
|
-
transpose_where_clauses(source_shard, target_shard, remove_nonlocal_primary_keys)
|
126
|
-
transpose_having_clauses(source_shard, target_shard, remove_nonlocal_primary_keys)
|
111
|
+
def transpose_clauses(source_shard, target_shard, remove_nonlocal_primary_keys: false)
|
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)
|
127
114
|
end
|
128
115
|
|
129
|
-
def infer_shards_from_primary_key(predicates
|
116
|
+
def infer_shards_from_primary_key(predicates)
|
130
117
|
return unless klass.integral_id?
|
131
118
|
|
132
119
|
primary_key = predicates.detect do |predicate|
|
133
|
-
predicate.is_a?(::Arel::Nodes::Binary)
|
134
|
-
predicate.left.
|
120
|
+
(predicate.is_a?(::Arel::Nodes::Binary) || predicate.is_a?(::Arel::Nodes::HomogeneousIn)) &&
|
121
|
+
predicate.left.is_a?(::Arel::Attributes::Attribute) &&
|
122
|
+
predicate.left.relation.is_a?(::Arel::Table) && predicate.left.relation.klass == klass &&
|
135
123
|
klass.primary_key == predicate.left.name
|
136
124
|
end
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
if
|
147
|
-
|
148
|
-
elsif id_shards.length == 1
|
149
|
-
id_shard = id_shards.first
|
150
|
-
# prefer to not change the shard
|
151
|
-
elsif id_shards.include?(primary_shard)
|
152
|
-
id_shards.delete(primary_shard)
|
153
|
-
self.shard_value = [primary_shard] + id_shards.to_a
|
154
|
-
return
|
155
|
-
else
|
156
|
-
id_shards = id_shards.to_a
|
157
|
-
transpose_clauses(primary_shard, id_shards.first)
|
158
|
-
self.shard_value = id_shards
|
159
|
-
return
|
160
|
-
end
|
161
|
-
when ::Arel::Nodes::BindParam
|
162
|
-
if ::Rails.version >= "5.2"
|
163
|
-
local_id, id_shard = Shard.local_id_for(primary_key.right.value.value_before_type_cast)
|
164
|
-
id_shard ||= Shard.current(klass.shard_category) if local_id
|
165
|
-
else
|
166
|
-
# look for a bind param with a matching column name
|
167
|
-
if binds && bind = binds.detect{|b| b&.name.to_s == klass.primary_key.to_s}
|
168
|
-
unless bind.value.is_a?(::ActiveRecord::StatementCache::Substitute)
|
169
|
-
local_id, id_shard = Shard.local_id_for(bind.value)
|
170
|
-
id_shard ||= Shard.current(klass.shard_category) if local_id
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
else
|
175
|
-
local_id, id_shard = Shard.local_id_for(primary_key.right)
|
176
|
-
id_shard ||= Shard.current(klass.shard_category) if local_id
|
125
|
+
return unless primary_key
|
126
|
+
|
127
|
+
right = primary_key.is_a?(::Arel::Nodes::HomogeneousIn) ? primary_key.values : primary_key.right
|
128
|
+
|
129
|
+
case right
|
130
|
+
when Array
|
131
|
+
id_shards = Set.new
|
132
|
+
right.each do |value|
|
133
|
+
local_id, id_shard = Shard.local_id_for(value)
|
134
|
+
id_shard ||= Shard.current(klass.connection_classes) if local_id
|
135
|
+
id_shards << id_shard if id_shard
|
177
136
|
end
|
137
|
+
return if id_shards.empty?
|
178
138
|
|
179
|
-
|
180
|
-
|
181
|
-
|
139
|
+
if id_shards.length == 1
|
140
|
+
id_shard = id_shards.first
|
141
|
+
# prefer to not change the shard
|
142
|
+
elsif id_shards.include?(primary_shard)
|
143
|
+
id_shards.delete(primary_shard)
|
144
|
+
self.shard_value = [primary_shard] + id_shards.to_a
|
145
|
+
return
|
146
|
+
else
|
147
|
+
id_shards = id_shards.to_a
|
148
|
+
transpose_clauses(primary_shard, id_shards.first)
|
149
|
+
self.shard_value = id_shards
|
150
|
+
return
|
151
|
+
end
|
152
|
+
when ::Arel::Nodes::BindParam
|
153
|
+
local_id, id_shard = Shard.local_id_for(right.value.value_before_type_cast)
|
154
|
+
id_shard ||= Shard.current(klass.connection_classes) if local_id
|
155
|
+
else
|
156
|
+
local_id, id_shard = Shard.local_id_for(right)
|
157
|
+
id_shard ||= Shard.current(klass.connection_classes) if local_id
|
182
158
|
end
|
159
|
+
|
160
|
+
return if !id_shard || id_shard == primary_shard
|
161
|
+
|
162
|
+
transpose_clauses(primary_shard, id_shard)
|
163
|
+
self.shard_value = id_shard
|
183
164
|
end
|
184
165
|
|
185
166
|
def transposable_attribute_type(relation, column)
|
@@ -201,8 +182,9 @@ module Switchman
|
|
201
182
|
|
202
183
|
def sharded_primary_key?(relation, column)
|
203
184
|
column = column.to_s
|
204
|
-
return column == 'id' if relation.
|
205
|
-
|
185
|
+
return column == 'id' if relation.klass == ::ActiveRecord::Base
|
186
|
+
|
187
|
+
relation.klass.primary_key == column && relation.klass.integral_id?
|
206
188
|
end
|
207
189
|
|
208
190
|
def source_shard_for_foreign_key(relation, column)
|
@@ -211,8 +193,9 @@ module Switchman
|
|
211
193
|
reflection = model.send(:reflection_for_integer_attribute, column)
|
212
194
|
break if reflection
|
213
195
|
end
|
214
|
-
return Shard.current(klass.
|
215
|
-
|
196
|
+
return Shard.current(klass.connection_classes) if reflection.options[:polymorphic]
|
197
|
+
|
198
|
+
Shard.current(reflection.klass.connection_classes)
|
216
199
|
end
|
217
200
|
|
218
201
|
def relation_and_column(attribute)
|
@@ -221,8 +204,31 @@ module Switchman
|
|
221
204
|
[attribute.relation, column]
|
222
205
|
end
|
223
206
|
|
224
|
-
def
|
225
|
-
|
207
|
+
def build_where_clause(opts, rest = [])
|
208
|
+
opts = sanitize_forbidden_attributes(opts)
|
209
|
+
|
210
|
+
case opts
|
211
|
+
when String, Array
|
212
|
+
values = Hash === rest.first ? rest.first.values : rest
|
213
|
+
|
214
|
+
values.grep(ActiveRecord::Relation) do |rel|
|
215
|
+
# serialize subqueries against the same shard as the outer query is currently
|
216
|
+
# targeted to run against
|
217
|
+
rel.shard!(primary_shard) if rel.shard_source_value == :implicit && rel.primary_shard != primary_shard
|
218
|
+
end
|
219
|
+
|
220
|
+
super
|
221
|
+
when Hash, ::Arel::Nodes::Node
|
222
|
+
where_clause = super
|
223
|
+
|
224
|
+
predicates = where_clause.send(:predicates)
|
225
|
+
infer_shards_from_primary_key(predicates) if shard_source_value == :implicit && shard_value.is_a?(Shard)
|
226
|
+
predicates = transpose_predicates(predicates, nil, primary_shard)
|
227
|
+
where_clause.instance_variable_set(:@predicates, predicates)
|
228
|
+
where_clause
|
229
|
+
else
|
230
|
+
super
|
231
|
+
end
|
226
232
|
end
|
227
233
|
|
228
234
|
def arel_columns(columns)
|
@@ -233,117 +239,86 @@ module Switchman
|
|
233
239
|
connection.with_local_table_name { super }
|
234
240
|
end
|
235
241
|
|
236
|
-
def table_name_matches?(from)
|
237
|
-
connection.with_global_table_name { super }
|
238
|
-
end
|
239
|
-
|
240
|
-
# semi-private
|
241
|
-
public
|
242
242
|
def transpose_predicates(predicates,
|
243
243
|
source_shard,
|
244
244
|
target_shard,
|
245
|
-
remove_nonlocal_primary_keys
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
remove_nonlocal_primary_keys,
|
252
|
-
binds: binds,
|
253
|
-
dup_binds_on_mutation: dup_binds_on_mutation)
|
254
|
-
next (if new_predicates == predicate.children
|
255
|
-
predicate
|
256
|
-
else
|
257
|
-
::Arel::Nodes::And.new(new_predicates)
|
258
|
-
end)
|
259
|
-
end
|
245
|
+
remove_nonlocal_primary_keys: false)
|
246
|
+
predicates.map do |predicate|
|
247
|
+
transpose_single_predicate(predicate, source_shard, target_shard,
|
248
|
+
remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
249
|
+
end
|
250
|
+
end
|
260
251
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
remove_nonlocal_primary_keys &&
|
268
|
-
predicate.left.relation.model == klass &&
|
269
|
-
(predicate.is_a?(::Arel::Nodes::Equality) || predicate.is_a?(::Arel::Nodes::In))
|
270
|
-
|
271
|
-
current_source_shard =
|
272
|
-
if source_shard
|
273
|
-
source_shard
|
274
|
-
elsif type == :primary
|
275
|
-
Shard.current(klass.shard_category)
|
276
|
-
elsif type == :foreign
|
277
|
-
source_shard_for_foreign_key(relation, column)
|
278
|
-
end
|
252
|
+
def transpose_single_predicate(predicate,
|
253
|
+
source_shard,
|
254
|
+
target_shard,
|
255
|
+
remove_nonlocal_primary_keys: false)
|
256
|
+
if predicate.is_a?(::Arel::Nodes::Grouping)
|
257
|
+
return predicate unless predicate.expr.is_a?(::Arel::Nodes::Or)
|
279
258
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
bind.value.primary = true if type == :primary
|
259
|
+
# Dang, we have an OR. OK, that means we have other epxressions below this
|
260
|
+
# level, perhaps many, that may need transposition.
|
261
|
+
# the left side and right side must each be treated as predicate lists and
|
262
|
+
# transformed in kind, if neither of them changes we can just return the grouping as is.
|
263
|
+
# hold on, it's about to get recursive...
|
264
|
+
or_expr = predicate.expr
|
265
|
+
left_node = or_expr.left
|
266
|
+
right_node = or_expr.right
|
267
|
+
new_left_predicates = transpose_single_predicate(left_node, source_shard,
|
268
|
+
target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
269
|
+
or_expr.instance_variable_set(:@left, new_left_predicates) if new_left_predicates != left_node
|
270
|
+
new_right_predicates = transpose_single_predicate(right_node, source_shard,
|
271
|
+
target_shard, remove_nonlocal_primary_keys: remove_nonlocal_primary_keys)
|
272
|
+
or_expr.instance_variable_set(:@right, new_right_predicates) if new_right_predicates != right_node
|
273
|
+
return predicate
|
274
|
+
end
|
275
|
+
return predicate unless predicate.is_a?(::Arel::Nodes::Binary) || predicate.is_a?(::Arel::Nodes::HomogeneousIn)
|
276
|
+
return predicate unless predicate.left.is_a?(::Arel::Attributes::Attribute)
|
277
|
+
|
278
|
+
relation, column = relation_and_column(predicate.left)
|
279
|
+
return predicate unless (type = transposable_attribute_type(relation, column))
|
280
|
+
|
281
|
+
remove = true if type == :primary &&
|
282
|
+
remove_nonlocal_primary_keys &&
|
283
|
+
predicate.left.relation.klass == klass &&
|
284
|
+
predicate.is_a?(::Arel::Nodes::Equality)
|
285
|
+
|
286
|
+
current_source_shard =
|
287
|
+
if source_shard
|
288
|
+
source_shard
|
289
|
+
elsif type == :primary
|
290
|
+
Shard.current(klass.connection_classes)
|
291
|
+
elsif type == :foreign
|
292
|
+
source_shard_for_foreign_key(relation, column)
|
293
|
+
end
|
294
|
+
|
295
|
+
right = if predicate.is_a?(::Arel::Nodes::HomogeneousIn)
|
296
|
+
predicate.values
|
319
297
|
else
|
320
|
-
|
321
|
-
local_id = [] if remove && local_id > Shard::IDS_PER_SHARD
|
322
|
-
bind.instance_variable_set(:@value, local_id)
|
323
|
-
bind.instance_variable_set(:@value_for_database, nil)
|
298
|
+
predicate.right
|
324
299
|
end
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
300
|
+
|
301
|
+
new_right_value =
|
302
|
+
case right
|
303
|
+
when Array
|
304
|
+
right.map { |val| transpose_predicate_value(val, current_source_shard, target_shard, type, remove) }
|
305
|
+
else
|
306
|
+
transpose_predicate_value(right, current_source_shard, target_shard, type, remove)
|
332
307
|
end
|
333
|
-
|
308
|
+
|
309
|
+
if new_right_value == right
|
310
|
+
predicate
|
311
|
+
elsif predicate.right.is_a?(::Arel::Nodes::Casted)
|
312
|
+
if new_right_value == right.value
|
334
313
|
predicate
|
335
|
-
elsif predicate.right.is_a?(::Arel::Nodes::Casted)
|
336
|
-
if new_right_value == predicate.right.val
|
337
|
-
predicate
|
338
|
-
else
|
339
|
-
predicate.class.new(predicate.left, predicate.right.class.new(new_right_value, predicate.right.attribute))
|
340
|
-
end
|
341
314
|
else
|
342
|
-
predicate.class.new(predicate.left, new_right_value)
|
315
|
+
predicate.class.new(predicate.left, right.class.new(new_right_value, right.attribute))
|
343
316
|
end
|
317
|
+
elsif predicate.is_a?(::Arel::Nodes::HomogeneousIn)
|
318
|
+
predicate.class.new(new_right_value, predicate.attribute, predicate.type)
|
319
|
+
else
|
320
|
+
predicate.class.new(predicate.left, new_right_value)
|
344
321
|
end
|
345
|
-
result = [result, binds]
|
346
|
-
result
|
347
322
|
end
|
348
323
|
|
349
324
|
def transpose_predicate_value(value, current_shard, target_shard, attribute_type, remove_non_local_ids)
|
@@ -356,12 +331,12 @@ module Switchman
|
|
356
331
|
value
|
357
332
|
else
|
358
333
|
local_id = Shard.relative_id_for(current_id, current_shard, target_shard) || current_id
|
359
|
-
|
360
|
-
if current_id
|
334
|
+
local_id = [] if remove_non_local_ids && local_id.is_a?(Integer) && local_id > Shard::IDS_PER_SHARD
|
335
|
+
if current_id == local_id
|
361
336
|
# make a new bind param
|
362
|
-
::Arel::Nodes::BindParam.new(query_att.class.new(query_att.name, local_id, query_att.type))
|
363
|
-
else
|
364
337
|
value
|
338
|
+
else
|
339
|
+
::Arel::Nodes::BindParam.new(query_att.class.new(query_att.name, local_id, query_att.type))
|
365
340
|
end
|
366
341
|
end
|
367
342
|
else
|
@@ -5,7 +5,7 @@ module Switchman
|
|
5
5
|
module Reflection
|
6
6
|
module AbstractReflection
|
7
7
|
def shard(owner)
|
8
|
-
if polymorphic? || klass.
|
8
|
+
if polymorphic? || klass.connection_classes == owner.class.connection_classes
|
9
9
|
# polymorphic associations assume the same shard as the owning item
|
10
10
|
owner.shard
|
11
11
|
else
|
@@ -28,32 +28,17 @@ module Switchman
|
|
28
28
|
# this technically belongs on AssociationReflection, but we put it on
|
29
29
|
# ThroughReflection as well, instead of delegating to its internal
|
30
30
|
# HasManyAssociation, losing its proper `klass`
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
key = [key, shard(owner).id].flatten
|
38
|
-
@association_scope_cache[key] ||= @scope_lock.synchronize {
|
39
|
-
@association_scope_cache[key] ||= (::Rails.version >= "5.2" ? ::ActiveRecord::StatementCache.create(conn, &block) : block.call)
|
40
|
-
}
|
41
|
-
end
|
42
|
-
else
|
43
|
-
def association_scope_cache(klass, owner, &block)
|
44
|
-
key = self
|
45
|
-
if polymorphic?
|
46
|
-
key = [key, owner._read_attribute(@foreign_type)]
|
47
|
-
end
|
48
|
-
key = [key, shard(owner).id].flatten
|
49
|
-
klass.cached_find_by_statement(key, &block)
|
50
|
-
end
|
31
|
+
def association_scope_cache(klass, owner, &block)
|
32
|
+
key = self
|
33
|
+
key = [key, owner._read_attribute(@foreign_type)] if polymorphic?
|
34
|
+
key = [key, shard(owner).id].flatten
|
35
|
+
klass.cached_find_by_statement(key, &block)
|
51
36
|
end
|
52
37
|
end
|
53
38
|
|
54
39
|
module AssociationReflection
|
55
40
|
def join_id_for(owner)
|
56
|
-
owner.send(
|
41
|
+
owner.send(join_foreign_key) # use sharded id values in association binds
|
57
42
|
end
|
58
43
|
end
|
59
44
|
end
|