switchman 2.1.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +10 -2
  3. data/app/models/switchman/shard.rb +234 -282
  4. data/app/models/switchman/unsharded_record.rb +7 -0
  5. data/db/migrate/20130328212039_create_switchman_shards.rb +1 -1
  6. data/db/migrate/20130328224244_create_default_shard.rb +5 -5
  7. data/db/migrate/20161206323434_add_back_default_string_limits_switchman.rb +1 -0
  8. data/db/migrate/20180828183945_add_default_shard_index.rb +2 -2
  9. data/db/migrate/20180828192111_add_timestamps_to_shards.rb +7 -5
  10. data/db/migrate/20190114212900_add_unique_name_indexes.rb +5 -3
  11. data/lib/switchman/action_controller/caching.rb +2 -2
  12. data/lib/switchman/active_record/abstract_adapter.rb +1 -0
  13. data/lib/switchman/active_record/association.rb +78 -89
  14. data/lib/switchman/active_record/attribute_methods.rb +58 -52
  15. data/lib/switchman/active_record/base.rb +58 -59
  16. data/lib/switchman/active_record/calculations.rb +74 -67
  17. data/lib/switchman/active_record/connection_pool.rb +14 -41
  18. data/lib/switchman/active_record/database_configurations/database_config.rb +13 -0
  19. data/lib/switchman/active_record/database_configurations.rb +34 -0
  20. data/lib/switchman/active_record/finder_methods.rb +11 -16
  21. data/lib/switchman/active_record/log_subscriber.rb +4 -8
  22. data/lib/switchman/active_record/migration.rb +6 -47
  23. data/lib/switchman/active_record/model_schema.rb +1 -1
  24. data/lib/switchman/active_record/persistence.rb +4 -6
  25. data/lib/switchman/active_record/postgresql_adapter.rb +124 -168
  26. data/lib/switchman/active_record/predicate_builder.rb +2 -2
  27. data/lib/switchman/active_record/query_cache.rb +18 -19
  28. data/lib/switchman/active_record/query_methods.rb +172 -197
  29. data/lib/switchman/active_record/reflection.rb +7 -22
  30. data/lib/switchman/active_record/relation.rb +30 -78
  31. data/lib/switchman/active_record/spawn_methods.rb +27 -29
  32. data/lib/switchman/active_record/statement_cache.rb +18 -35
  33. data/lib/switchman/active_record/tasks/database_tasks.rb +16 -0
  34. data/lib/switchman/active_support/cache.rb +3 -5
  35. data/lib/switchman/arel.rb +13 -8
  36. data/lib/switchman/database_server.rb +121 -142
  37. data/lib/switchman/default_shard.rb +52 -16
  38. data/lib/switchman/engine.rb +61 -58
  39. data/lib/switchman/environment.rb +4 -8
  40. data/lib/switchman/errors.rb +1 -0
  41. data/lib/switchman/guard_rail/relation.rb +5 -7
  42. data/lib/switchman/guard_rail.rb +6 -19
  43. data/lib/switchman/r_spec_helper.rb +29 -37
  44. data/lib/switchman/rails.rb +14 -12
  45. data/lib/switchman/schema_cache.rb +1 -9
  46. data/lib/switchman/sharded_instrumenter.rb +1 -1
  47. data/lib/switchman/standard_error.rb +15 -3
  48. data/lib/switchman/test_helper.rb +6 -4
  49. data/lib/switchman/version.rb +1 -1
  50. data/lib/switchman.rb +3 -5
  51. data/lib/tasks/switchman.rake +54 -69
  52. metadata +87 -45
  53. data/lib/switchman/active_record/batches.rb +0 -11
  54. data/lib/switchman/active_record/connection_handler.rb +0 -190
  55. data/lib/switchman/active_record/where_clause_factory.rb +0 -36
  56. 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
- old_primary_shard = self.primary_shard
43
+
44
+ old_primary_shard = primary_shard
39
45
  self.shard_value = value
40
46
  self.shard_source_value = source
41
- if (old_primary_shard != self.primary_shard || source == :to_a)
42
- transpose_clauses(old_primary_shard, self.primary_shard, source == :to_a)
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.shard_category)
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.shard_category)]
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
- if ::Rails.version >= '5.2'
84
- [:where, :having].each do |type|
85
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
86
- def transpose_#{type}_clauses(source_shard, target_shard, remove_nonlocal_primary_keys)
87
- unless (predicates = #{type}_clause.send(:predicates)).empty?
88
- new_predicates, _binds = transpose_predicates(predicates, source_shard,
89
- target_shard, remove_nonlocal_primary_keys)
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
- self.#{type}_clause = #{type}_clause.dup
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 = false)
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, binds = nil)
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) && predicate.left.is_a?(::Arel::Attributes::Attribute) &&
134
- predicate.left.relation.is_a?(::Arel::Table) && predicate.left.relation.model == klass &&
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
- if primary_key
138
- case primary_key.right
139
- when Array
140
- id_shards = Set.new
141
- primary_key.right.each do |value|
142
- local_id, id_shard = Shard.local_id_for(value)
143
- id_shard ||= Shard.current(klass.shard_category) if local_id
144
- id_shards << id_shard if id_shard
145
- end
146
- if id_shards.empty?
147
- return
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
- return if !id_shard || id_shard == primary_shard
180
- transpose_clauses(primary_shard, id_shard)
181
- self.shard_value = id_shard
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.model == ::ActiveRecord::Base
205
- relation.model.primary_key == column && relation.model.integral_id?
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.shard_category) if reflection.options[:polymorphic]
215
- Shard.current(reflection.klass.shard_category)
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 where_clause_factory
225
- super.tap { |factory| factory.scope = self }
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 = false,
246
- binds: nil,
247
- dup_binds_on_mutation: false)
248
- result = predicates.map do |predicate|
249
- if ::Rails.version >= '5.2' && predicate.is_a?(::Arel::Nodes::And)
250
- new_predicates, _binds = transpose_predicates(predicate.children, source_shard, target_shard,
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
- next predicate unless predicate.is_a?(::Arel::Nodes::Binary)
262
- next predicate unless predicate.left.is_a?(::Arel::Attributes::Attribute)
263
- relation, column = relation_and_column(predicate.left)
264
- next predicate unless (type = transposable_attribute_type(relation, column))
265
-
266
- remove = true if type == :primary &&
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
- if ::Rails.version >= "5.2"
281
- new_right_value =
282
- case predicate.right
283
- when Array
284
- predicate.right.map {|val| transpose_predicate_value(val, current_source_shard, target_shard, type, remove).presence }.compact
285
- else
286
- transpose_predicate_value(predicate.right, current_source_shard, target_shard, type, remove)
287
- end
288
- else
289
- new_right_value = case predicate.right
290
- when Array
291
- local_ids = []
292
- predicate.right.each do |value|
293
- local_id = Shard.relative_id_for(value, current_source_shard, target_shard)
294
- next unless local_id
295
- unless remove && local_id > Shard::IDS_PER_SHARD
296
- if value.is_a?(::Arel::Nodes::Casted)
297
- if local_id == value.val
298
- local_id = value
299
- elsif local_id != value
300
- local_id = value.class.new(local_id, value.attribute)
301
- end
302
- end
303
- local_ids << local_id
304
- end
305
- end
306
- local_ids
307
- when ::Arel::Nodes::BindParam
308
- # look for a bind param with a matching column name
309
- if binds && bind = binds.detect{|b| b&.name.to_s == predicate.left.name.to_s}
310
- # before we mutate, dup
311
- if dup_binds_on_mutation
312
- binds = binds.map(&:dup)
313
- dup_binds_on_mutation = false
314
- bind = binds.find { |b| b&.name.to_s == predicate.left.name.to_s }
315
- end
316
- if bind.value.is_a?(::ActiveRecord::StatementCache::Substitute)
317
- bind.value.sharded = true # mark for transposition later
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
- local_id = Shard.relative_id_for(bind.value, current_source_shard, target_shard)
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
- end
326
- predicate.right
327
- else
328
- local_id = Shard.relative_id_for(predicate.right, current_source_shard, target_shard) || predicate.right
329
- local_id = [] if remove && local_id.is_a?(Integer) && local_id > Shard::IDS_PER_SHARD
330
- local_id
331
- end
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
- if new_right_value == predicate.right
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
- return nil if remove_non_local_ids && local_id.is_a?(Integer) && local_id > Shard::IDS_PER_SHARD
360
- if current_id != local_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.shard_category == owner.class.shard_category
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
- if ::Rails.version < '6.0.4'
32
- def association_scope_cache(conn, owner, &block)
33
- key = conn.prepared_statements
34
- if polymorphic?
35
- key = [key, owner._read_attribute(@foreign_type)]
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(::Rails.version >= "5.2" ? join_foreign_key : active_record_primary_key) # use sharded id values in association binds
41
+ owner.send(join_foreign_key) # use sharded id values in association binds
57
42
  end
58
43
  end
59
44
  end