switchman 3.4.2 → 3.6.7

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +15 -14
  3. data/db/migrate/20180828183945_add_default_shard_index.rb +1 -1
  4. data/db/migrate/20190114212900_add_unique_name_indexes.rb +10 -4
  5. data/lib/switchman/active_record/abstract_adapter.rb +4 -2
  6. data/lib/switchman/active_record/associations.rb +89 -16
  7. data/lib/switchman/active_record/attribute_methods.rb +67 -22
  8. data/lib/switchman/active_record/base.rb +112 -22
  9. data/lib/switchman/active_record/calculations.rb +93 -37
  10. data/lib/switchman/active_record/connection_handler.rb +18 -0
  11. data/lib/switchman/active_record/connection_pool.rb +18 -14
  12. data/lib/switchman/active_record/database_configurations.rb +37 -15
  13. data/lib/switchman/active_record/finder_methods.rb +44 -14
  14. data/lib/switchman/active_record/log_subscriber.rb +11 -5
  15. data/lib/switchman/active_record/migration.rb +28 -9
  16. data/lib/switchman/active_record/pending_migration_connection.rb +17 -0
  17. data/lib/switchman/active_record/persistence.rb +22 -0
  18. data/lib/switchman/active_record/postgresql_adapter.rb +11 -10
  19. data/lib/switchman/active_record/predicate_builder.rb +2 -2
  20. data/lib/switchman/active_record/query_cache.rb +49 -20
  21. data/lib/switchman/active_record/query_methods.rb +93 -30
  22. data/lib/switchman/active_record/relation.rb +22 -11
  23. data/lib/switchman/active_record/spawn_methods.rb +2 -2
  24. data/lib/switchman/active_record/statement_cache.rb +2 -2
  25. data/lib/switchman/active_record/tasks/database_tasks.rb +6 -1
  26. data/lib/switchman/active_record/test_fixtures.rb +26 -16
  27. data/lib/switchman/active_support/cache.rb +9 -4
  28. data/lib/switchman/arel.rb +34 -18
  29. data/lib/switchman/call_super.rb +2 -8
  30. data/lib/switchman/database_server.rb +68 -21
  31. data/lib/switchman/default_shard.rb +14 -3
  32. data/lib/switchman/engine.rb +39 -19
  33. data/lib/switchman/environment.rb +2 -2
  34. data/lib/switchman/errors.rb +4 -1
  35. data/lib/switchman/guard_rail/relation.rb +1 -2
  36. data/lib/switchman/parallel.rb +5 -5
  37. data/lib/switchman/r_spec_helper.rb +11 -11
  38. data/lib/switchman/shard.rb +166 -64
  39. data/lib/switchman/sharded_instrumenter.rb +7 -3
  40. data/lib/switchman/standard_error.rb +4 -0
  41. data/lib/switchman/test_helper.rb +2 -2
  42. data/lib/switchman/version.rb +1 -1
  43. data/lib/switchman.rb +27 -15
  44. data/lib/tasks/switchman.rake +117 -51
  45. metadata +19 -44
@@ -22,13 +22,59 @@ module Switchman
22
22
  @integral_id
23
23
  end
24
24
 
25
- %w[transaction insert_all upsert_all].each do |method|
25
+ def transaction(**)
26
+ if self != ::ActiveRecord::Base && current_scope
27
+ current_scope.activate do
28
+ db = Shard.current(connection_class_for_self).database_server
29
+ db.unguard { super }
30
+ end
31
+ else
32
+ db = Shard.current(connection_class_for_self).database_server
33
+ db.unguard { super }
34
+ end
35
+ end
36
+
37
+ # NOTE: `returning` values are _not_ transposed back to the current shard
38
+ %w[insert_all upsert_all].each do |method|
26
39
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
27
- def #{method}(*, **)
28
- if self != ::ActiveRecord::Base && current_scope
29
- current_scope.activate do
40
+ def #{method}(attributes, returning: nil, **)
41
+ scope = self != ::ActiveRecord::Base && current_scope
42
+ if (target_shard = scope&.primary_shard) == (current_shard = Shard.current(connection_class_for_self))
43
+ scope = nil
44
+ end
45
+ if scope
46
+ dupped = false
47
+ attributes.each_with_index do |hash, i|
48
+ if dupped || hash.any? { |k, v| sharded_column?(k) }
49
+ unless dupped
50
+ attributes = attributes.dup
51
+ dupped = true
52
+ end
53
+ attributes[i] = hash.to_h do |k, v|
54
+ if sharded_column?(k)
55
+ [k, Shard.relative_id_for(v, current_shard, target_shard)]
56
+ else
57
+ [k, v]
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ if scope
65
+ scope.activate do
30
66
  db = Shard.current(connection_class_for_self).database_server
31
- db.unguard { super }
67
+ result = db.unguard { super }
68
+ if result&.columns&.any? { |c| sharded_column?(c) }
69
+ transposed_rows = result.rows.map do |row|
70
+ row.map.with_index do |value, i|
71
+ sharded_column?(result.columns[i]) ? Shard.relative_id_for(value, target_shard, current_shard) : value
72
+ end
73
+ end
74
+ result = ::ActiveRecord::Result.new(result.columns, transposed_rows, result.column_types)
75
+ end
76
+
77
+ result
32
78
  end
33
79
  else
34
80
  db = Shard.current(connection_class_for_self).database_server
@@ -57,7 +103,12 @@ module Switchman
57
103
  end
58
104
 
59
105
  def clear_query_caches_for_current_thread
60
- ::ActiveRecord::Base.connection_handler.connection_pool_list.each do |pool|
106
+ pools = if ::Rails.version < "7.1"
107
+ ::ActiveRecord::Base.connection_handler.connection_pool_list
108
+ else
109
+ ::ActiveRecord::Base.connection_handler.connection_pool_list(:all)
110
+ end
111
+ pools.each do |pool|
61
112
  pool.connection(switch_shard: false).clear_query_cache if pool.active_connection?
62
113
  end
63
114
  end
@@ -67,7 +118,10 @@ module Switchman
67
118
  end
68
119
 
69
120
  def establish_connection(config_or_env = nil)
70
- raise ArgumentError, 'establish connection cannot be used on the non-current shard/role' if config_or_env.is_a?(Symbol) && config_or_env != ::Rails.env.to_sym
121
+ if config_or_env.is_a?(Symbol) && config_or_env != ::Rails.env.to_sym
122
+ raise ArgumentError,
123
+ "establish connection cannot be used on the non-current shard/role"
124
+ end
71
125
 
72
126
  # Ensure we don't randomly surprise change the connection parms associated with a shard/role
73
127
  config_or_env = nil if config_or_env == ::Rails.env.to_sym
@@ -75,16 +129,22 @@ module Switchman
75
129
  config_or_env ||= if current_shard == ::Rails.env.to_sym && current_role == :primary
76
130
  :primary
77
131
  else
78
- "#{current_shard}/#{current_role}".to_sym
132
+ :"#{current_shard}/#{current_role}"
79
133
  end
80
134
 
81
135
  super(config_or_env)
82
136
  end
83
137
 
84
138
  def connected_to_stack
85
- return super if ::Rails.version < '7.0' ? Thread.current.thread_variable?(:ar_connected_to_stack) : ::ActiveSupport::IsolatedExecutionState.key?(:active_record_connected_to_stack)
139
+ has_own_stack = if ::Rails.version < "7.0"
140
+ Thread.current.thread_variable?(:ar_connected_to_stack)
141
+ else
142
+ ::ActiveSupport::IsolatedExecutionState.key?(:active_record_connected_to_stack)
143
+ end
86
144
 
87
145
  ret = super
146
+ return ret if has_own_stack
147
+
88
148
  DatabaseServer.guard_servers
89
149
  ret
90
150
  end
@@ -96,10 +156,13 @@ module Switchman
96
156
  sharded_role = nil
97
157
  connected_to_stack.reverse_each do |hash|
98
158
  shard_role = hash.dig(:shard_roles, target_shard)
99
- if shard_role && (hash[:klasses].include?(::ActiveRecord::Base) || hash[:klasses].include?(connection_class_for_self))
100
- sharded_role = shard_role
101
- break
159
+ unless shard_role &&
160
+ (hash[:klasses].include?(::ActiveRecord::Base) || hash[:klasses].include?(connection_class_for_self))
161
+ next
102
162
  end
163
+
164
+ sharded_role = shard_role
165
+ break
103
166
  end
104
167
  # Allow a shard-specific role to be reverted to regular inheritance
105
168
  return sharded_role if sharded_role && sharded_role != :_switchman_inherit
@@ -119,13 +182,15 @@ module Switchman
119
182
 
120
183
  def current_switchman_shard
121
184
  connected_to_stack.reverse_each do |hash|
122
- return hash[:switchman_shard] if hash[:switchman_shard] && hash[:klasses].include?(connection_class_for_self)
185
+ if hash[:switchman_shard] && hash[:klasses].include?(connection_class_for_self)
186
+ return hash[:switchman_shard]
187
+ end
123
188
  end
124
189
 
125
190
  Shard.default
126
191
  end
127
192
 
128
- if ::Rails.version < '7.0'
193
+ if ::Rails.version < "7.0"
129
194
  def connection_class_for_self
130
195
  connection_classes
131
196
  end
@@ -134,6 +199,13 @@ module Switchman
134
199
 
135
200
  def self.prepended(klass)
136
201
  klass.singleton_class.prepend(ClassMethods)
202
+ klass.scope :non_shadow, lambda { |key = primary_key|
203
+ where(key => (QueryMethods::NonTransposingValue.new(0)..
204
+ QueryMethods::NonTransposingValue.new(Shard::IDS_PER_SHARD)))
205
+ }
206
+ klass.scope :shadow, lambda { |key = primary_key|
207
+ where(key => QueryMethods::NonTransposingValue.new(Shard::IDS_PER_SHARD)..)
208
+ }
137
209
  end
138
210
 
139
211
  def _run_initialize_callbacks
@@ -144,7 +216,15 @@ module Switchman
144
216
  end
145
217
 
146
218
  @loaded_from_shard ||= Shard.current(self.class.connection_class_for_self)
147
- readonly! if shadow_record? && !Switchman.config[:writable_shadow_records]
219
+ if shadow_record? && !Switchman.config[:writable_shadow_records]
220
+ @readonly = true
221
+ @readonly_from_shadow ||= true
222
+ end
223
+ super
224
+ end
225
+
226
+ def readonly!
227
+ @readonly_from_shadow = false
148
228
  super
149
229
  end
150
230
 
@@ -155,6 +235,10 @@ module Switchman
155
235
  pkey > Shard::IDS_PER_SHARD
156
236
  end
157
237
 
238
+ def canonical?
239
+ !shadow_record?
240
+ end
241
+
158
242
  def save_shadow_record(new_attrs: attributes, target_shard: Shard.current)
159
243
  return if target_shard == shard
160
244
 
@@ -172,13 +256,17 @@ module Switchman
172
256
  end
173
257
 
174
258
  def destroy_shadow_records(target_shards: [Shard.current])
175
- raise Errors::ShadowRecordError, 'Cannot be called on a shadow record.' if shadow_record?
176
- raise Errors::MethodUnsupportedForUnshardedTableError, 'Cannot be called on a record belonging to an unsharded table.' unless self.class.sharded_column?(self.class.primary_key)
259
+ raise Errors::ShadowRecordError, "Cannot be called on a shadow record." if shadow_record?
260
+
261
+ unless self.class.sharded_column?(self.class.primary_key)
262
+ raise Errors::MethodUnsupportedForUnshardedTableError,
263
+ "Cannot be called on a record belonging to an unsharded table."
264
+ end
177
265
 
178
266
  Array(target_shards).each do |target_shard|
179
267
  next if target_shard == shard
180
268
 
181
- target_shard.activate { self.class.where('id = ?', global_id).delete_all }
269
+ target_shard.activate { self.class.where("id = ?", global_id).delete_all }
182
270
  end
183
271
  end
184
272
 
@@ -235,9 +323,9 @@ module Switchman
235
323
  result
236
324
  end
237
325
 
238
- def transaction(**kwargs, &block)
326
+ def transaction(...)
239
327
  shard.activate(self.class.connection_class_for_self) do
240
- self.class.transaction(**kwargs, &block)
328
+ self.class.transaction(...)
241
329
  end
242
330
  end
243
331
 
@@ -270,8 +358,10 @@ module Switchman
270
358
 
271
359
  def id_for_database
272
360
  if self.class.sharded_primary_key?
273
- # It's an int, so so it's safe to just return it without passing it through anything else
274
- # In theory we should do `@attributes[@primary_key].type.serialize(id)`, but that seems to have surprising side-effects
361
+ # It's an int, so it's safe to just return it without passing it
362
+ # through anything else. In theory we should do
363
+ # `@attributes[@primary_key].type.serialize(id)`, but that seems to
364
+ # have surprising side-effects
275
365
  id
276
366
  else
277
367
  super
@@ -28,7 +28,7 @@ module Switchman
28
28
 
29
29
  def execute_simple_calculation(operation, column_name, distinct)
30
30
  operation = operation.to_s.downcase
31
- if operation == 'average'
31
+ if operation == "average"
32
32
  result = calculate_simple_average(column_name, distinct)
33
33
  else
34
34
  result = activate do |relation|
@@ -36,11 +36,11 @@ module Switchman
36
36
  end
37
37
  if result.is_a?(Array)
38
38
  case operation
39
- when 'count', 'sum'
39
+ when "count", "sum"
40
40
  result = result.sum
41
- when 'minimum'
41
+ when "minimum"
42
42
  result = result.min
43
- when 'maximum'
43
+ when "maximum"
44
44
  result = result.max
45
45
  end
46
46
  end
@@ -52,20 +52,20 @@ module Switchman
52
52
  # See activerecord#execute_simple_calculation
53
53
  relation = except(:order)
54
54
  column = aggregate_column(column_name)
55
- relation.select_values = [operation_over_aggregate_column(column, 'average', distinct).as('average'),
56
- operation_over_aggregate_column(column, 'count', distinct).as('count')]
55
+ relation.select_values = [operation_over_aggregate_column(column, "average", distinct).as("average"),
56
+ operation_over_aggregate_column(column, "count", distinct).as("count")]
57
57
 
58
58
  initial_results = relation.activate { |rel| klass.connection.select_all(rel) }
59
59
  if initial_results.is_a?(Array)
60
60
  initial_results.each do |r|
61
- r['average'] = type_cast_calculated_value_switchman(r['average'], column_name, 'average')
62
- r['count'] = type_cast_calculated_value_switchman(r['count'], column_name, 'count')
61
+ r["average"] = type_cast_calculated_value_switchman(r["average"], column_name, "average")
62
+ r["count"] = type_cast_calculated_value_switchman(r["count"], column_name, "count")
63
63
  end
64
- result = initial_results.map { |r| r['average'] * r['count'] }.sum / initial_results.map do |r|
65
- r['count']
66
- end.sum
64
+ result = initial_results.sum { |r| r["average"] * r["count"] } / initial_results.sum do |r|
65
+ r["count"]
66
+ end
67
67
  else
68
- result = type_cast_calculated_value_switchman(initial_results.first['average'], column_name, 'average')
68
+ result = type_cast_calculated_value_switchman(initial_results.first["average"], column_name, "average")
69
69
  end
70
70
  result
71
71
  end
@@ -90,7 +90,7 @@ module Switchman
90
90
  row[opts[:aggregate_alias]] = type_cast_calculated_value_switchman(
91
91
  row[opts[:aggregate_alias]], column_name, opts[:operation]
92
92
  )
93
- row['count'] = row['count'].to_i if opts[:operation] == 'average'
93
+ row["count"] = row["count"].to_i if opts[:operation] == "average"
94
94
 
95
95
  opts[:group_columns].each do |aliaz, _type, group_column_name|
96
96
  if opts[:associated] && (aliaz == opts[:group_aliases].first)
@@ -106,10 +106,49 @@ module Switchman
106
106
  compact_grouped_calculation_rows(rows, opts)
107
107
  end
108
108
 
109
+ if ::Rails.version >= "7.1"
110
+ def ids
111
+ return super unless klass.sharded_primary_key?
112
+
113
+ if loaded?
114
+ result = records.map do |record|
115
+ Shard.relative_id_for(record._read_attribute(primary_key),
116
+ record.shard,
117
+ Shard.current(klass.connection_class_for_self))
118
+ end
119
+ return @async ? Promise::Complete.new(result) : result
120
+ end
121
+
122
+ if has_include?(primary_key)
123
+ relation = apply_join_dependency.group(primary_key)
124
+ return relation.ids
125
+ end
126
+
127
+ columns = arel_columns([primary_key])
128
+ base_shard = Shard.current(klass.connection_class_for_self)
129
+ activate do |r|
130
+ relation = r.spawn
131
+ relation.select_values = columns
132
+
133
+ result = if relation.where_clause.contradiction?
134
+ ::ActiveRecord::Result.empty
135
+ else
136
+ skip_query_cache_if_necessary do
137
+ klass.connection.select_all(relation, "#{klass.name} Ids", async: @async)
138
+ end
139
+ end
140
+
141
+ result.then do |res|
142
+ type_cast_pluck_values(res, columns).map { |id| Shard.relative_id_for(id, Shard.current, base_shard) }
143
+ end
144
+ end
145
+ end
146
+ end
147
+
109
148
  private
110
149
 
111
150
  def type_cast_calculated_value_switchman(value, column_name, operation)
112
- if ::Rails.version < '7.0'
151
+ if ::Rails.version < "7.0"
113
152
  type_cast_calculated_value(value, operation) do |val|
114
153
  column = aggregate_column(column_name)
115
154
  type ||= column.try(:type_caster) ||
@@ -125,39 +164,55 @@ module Switchman
125
164
  end
126
165
 
127
166
  def column_name_for(field)
128
- field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
167
+ field.respond_to?(:name) ? field.name.to_s : field.to_s.split(".").last
129
168
  end
130
169
 
131
170
  def grouped_calculation_options(operation, column_name, distinct)
132
171
  opts = { operation: operation, column_name: column_name, distinct: distinct }
133
172
 
134
- opts[:aggregate_alias] = aggregate_alias_for(operation, column_name)
173
+ # Rails 7.0.5
174
+ if defined?(::ActiveRecord::Calculations::ColumnAliasTracker)
175
+ column_alias_tracker = ::ActiveRecord::Calculations::ColumnAliasTracker.new(connection)
176
+ end
177
+
178
+ opts[:aggregate_alias] = aggregate_alias_for(operation, column_name, column_alias_tracker)
135
179
  group_attrs = group_values
136
180
  if group_attrs.first.respond_to?(:to_sym)
137
181
  association = klass.reflect_on_association(group_attrs.first.to_sym)
138
- associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
182
+ # only count belongs_to associations
183
+ associated = group_attrs.size == 1 && association && association.macro == :belongs_to
139
184
  group_fields = Array(associated ? association.foreign_key : group_attrs)
140
185
  else
141
186
  group_fields = group_attrs
142
187
  end
143
188
 
144
- # to_s is because Rails 5 returns a string but Rails 6 returns a symbol.
145
- group_aliases = group_fields.map { |field| column_alias_for(field.downcase.to_s).to_s }
189
+ group_aliases = group_fields.map do |field|
190
+ field = connection.visitor.compile(field) if ::Arel.arel_node?(field)
191
+ if column_alias_tracker
192
+ column_alias_tracker.alias_for(field.to_s.downcase)
193
+ else
194
+ column_alias_for(field.to_s.downcase)
195
+ end
196
+ end
146
197
  group_columns = group_aliases.zip(group_fields).map do |aliaz, field|
147
198
  [aliaz, type_for(field), column_name_for(field)]
148
199
  end
149
- opts.merge!(association: association, associated: associated,
150
- group_aliases: group_aliases, group_columns: group_columns,
200
+ opts.merge!(association: association,
201
+ associated: associated,
202
+ group_aliases: group_aliases,
203
+ group_columns: group_columns,
151
204
  group_fields: group_fields)
152
205
 
153
206
  opts
154
207
  end
155
208
 
156
- def aggregate_alias_for(operation, column_name)
157
- if operation == 'count' && column_name == :all
158
- 'count_all'
159
- elsif operation == 'average'
160
- 'average'
209
+ def aggregate_alias_for(operation, column_name, column_alias_tracker)
210
+ if operation == "count" && column_name == :all
211
+ "count_all"
212
+ elsif operation == "average"
213
+ "average"
214
+ elsif column_alias_tracker
215
+ column_alias_tracker.alias_for("#{operation} #{column_name}")
161
216
  else
162
217
  column_alias_for("#{operation} #{column_name}")
163
218
  end
@@ -173,13 +228,14 @@ module Switchman
173
228
  opts[:distinct]
174
229
  ).as(opts[:aggregate_alias])
175
230
  ]
176
- if opts[:operation] == 'average'
231
+ if opts[:operation] == "average"
177
232
  # include count in average so we can recalculate the average
178
233
  # across all shards if needed
179
234
  select_values << operation_over_aggregate_column(
180
235
  aggregate_column(opts[:column_name]),
181
- 'count', opts[:distinct]
182
- ).as('count')
236
+ "count",
237
+ opts[:distinct]
238
+ ).as("count")
183
239
  end
184
240
 
185
241
  haves = having_clause.send(:predicates)
@@ -205,22 +261,22 @@ module Switchman
205
261
  key = key.first if key.size == 1
206
262
  value = row[opts[:aggregate_alias]]
207
263
 
208
- if opts[:operation] == 'average'
264
+ if opts[:operation] == "average"
209
265
  if result.key?(key)
210
266
  old_value, old_count = result[key]
211
- new_count = old_count + row['count']
212
- new_value = ((old_value * old_count) + (value * row['count'])) / new_count
267
+ new_count = old_count + row["count"]
268
+ new_value = ((old_value * old_count) + (value * row["count"])) / new_count
213
269
  result[key] = [new_value, new_count]
214
270
  else
215
- result[key] = [value, row['count']]
271
+ result[key] = [value, row["count"]]
216
272
  end
217
273
  elsif result.key?(key)
218
274
  case opts[:operation]
219
- when 'count', 'sum'
275
+ when "count", "sum"
220
276
  result[key] += value
221
- when 'minimum'
277
+ when "minimum"
222
278
  result[key] = value if value < result[key]
223
- when 'maximum'
279
+ when "maximum"
224
280
  result[key] = value if value > result[key]
225
281
  end
226
282
  else
@@ -228,7 +284,7 @@ module Switchman
228
284
  end
229
285
  end
230
286
 
231
- result.transform_values!(&:first) if opts[:operation] == 'average'
287
+ result.transform_values!(&:first) if opts[:operation] == "average"
232
288
 
233
289
  result
234
290
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Switchman
4
+ module ActiveRecord
5
+ module ConnectionHandler
6
+ def resolve_pool_config(config, connection_name, role, shard)
7
+ ret = super
8
+ # Make *all* pool configs use the same schema reflection
9
+ ret.schema_reflection = ConnectionHandler.global_schema_reflection
10
+ ret
11
+ end
12
+
13
+ def self.global_schema_reflection
14
+ @global_schema_reflection ||= ::ActiveRecord::ConnectionAdapters::SchemaReflection.new(nil)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -3,22 +3,24 @@
3
3
  module Switchman
4
4
  module ActiveRecord
5
5
  module ConnectionPool
6
- def get_schema_cache(connection)
7
- self.schema_cache ||= SharedSchemaCache.get_schema_cache(connection)
8
- self.schema_cache.connection = connection
6
+ if ::Rails.version < "7.1"
7
+ def get_schema_cache(connection)
8
+ self.schema_cache ||= SharedSchemaCache.get_schema_cache(connection)
9
+ self.schema_cache.connection = connection
9
10
 
10
- self.schema_cache
11
- end
11
+ self.schema_cache
12
+ end
12
13
 
13
- # rubocop:disable Naming/AccessorMethodName override method
14
- def set_schema_cache(cache)
15
- schema_cache = get_schema_cache(cache.connection)
14
+ # rubocop:disable Naming/AccessorMethodName override method
15
+ def set_schema_cache(cache)
16
+ schema_cache = get_schema_cache(cache.connection)
16
17
 
17
- cache.instance_variables.each do |x|
18
- schema_cache.instance_variable_set(x, cache.instance_variable_get(x))
18
+ cache.instance_variables.each do |x|
19
+ schema_cache.instance_variable_set(x, cache.instance_variable_get(x))
20
+ end
19
21
  end
22
+ # rubocop:enable Naming/AccessorMethodName override method
20
23
  end
21
- # rubocop:enable Naming/AccessorMethodName override method
22
24
 
23
25
  def default_schema
24
26
  connection unless @schemas
@@ -48,7 +50,9 @@ module Switchman
48
50
  end
49
51
 
50
52
  def switch_database(conn)
51
- @schemas = conn.current_schemas if !@schemas && conn.adapter_name == 'PostgreSQL' && !current_shard.database_server.config[:shard_name]
53
+ if !@schemas && conn.adapter_name == "PostgreSQL" && !current_shard.database_server.config[:shard_name]
54
+ @schemas = conn.current_schemas
55
+ end
52
56
 
53
57
  conn.shard = current_shard
54
58
  end
@@ -56,11 +60,11 @@ module Switchman
56
60
  private
57
61
 
58
62
  def current_shard
59
- ::Rails.version < '7.0' ? connection_klass.current_switchman_shard : connection_class.current_switchman_shard
63
+ (::Rails.version < "7.0") ? connection_klass.current_switchman_shard : connection_class.current_switchman_shard
60
64
  end
61
65
 
62
66
  def tls_key
63
- "#{object_id}_shard".to_sym
67
+ :"#{object_id}_shard"
64
68
  end
65
69
  end
66
70
  end
@@ -7,14 +7,26 @@ module Switchman
7
7
  # since all should point to the same data, even if multiple are writable
8
8
  # (Picks 'primary' since it is guaranteed to exist and switchman handles activating
9
9
  # deploy through other means)
10
- def configs_for(include_replicas: false, name: nil, **)
11
- res = super
12
- if name && !include_replicas
13
- return nil unless name.end_with?('primary')
14
- elsif !include_replicas
15
- return res.select { |config| config.name.end_with?('primary') }
10
+ if ::Rails.version < "7.1"
11
+ def configs_for(include_replicas: false, name: nil, **)
12
+ res = super
13
+ if name && !include_replicas
14
+ return nil unless name.end_with?("primary")
15
+ elsif !include_replicas
16
+ return res.select { |config| config.name.end_with?("primary") }
17
+ end
18
+ res
19
+ end
20
+ else
21
+ def configs_for(include_hidden: false, name: nil, **)
22
+ res = super
23
+ if name && !include_hidden
24
+ return nil unless name.end_with?("primary")
25
+ elsif !include_hidden
26
+ return res.select { |config| config.name.end_with?("primary") }
27
+ end
28
+ res
16
29
  end
17
- res
18
30
  end
19
31
 
20
32
  private
@@ -27,21 +39,31 @@ module Switchman
27
39
  return configs if configs.is_a?(Array)
28
40
 
29
41
  db_configs = configs.flat_map do |env_name, config|
30
- # It would be nice to do the auto-fallback that we want here, but we haven't
31
- # actually done that for years (or maybe ever) and it will be a big lift to get working
32
- roles = config.keys.select { |k| config[k].is_a?(Hash) || (config[k].is_a?(Array) && config[k].all? { |ck| ck.is_a?(Hash) }) }
33
- base_config = config.except(*roles)
42
+ if config.is_a?(Hash)
43
+ # It would be nice to do the auto-fallback that we want here, but we haven't
44
+ # actually done that for years (or maybe ever) and it will be a big lift to get working
45
+ roles = config.keys.select do |k|
46
+ config[k].is_a?(Hash) || (config[k].is_a?(Array) && config[k].all?(Hash))
47
+ end
48
+ base_config = config.except(*roles)
49
+ else
50
+ base_config = config
51
+ roles = []
52
+ end
34
53
 
35
54
  name = "#{env_name}/primary"
36
- name = 'primary' if env_name == default_env
55
+ name = "primary" if env_name == default_env
37
56
  base_db = build_db_config_from_raw_config(env_name, name, base_config)
38
57
  [base_db] + roles.map do |role|
39
- build_db_config_from_raw_config(env_name, "#{env_name}/#{role}",
40
- base_config.merge(config[role].is_a?(Array) ? config[role].first : config[role]))
58
+ build_db_config_from_raw_config(
59
+ env_name,
60
+ "#{env_name}/#{role}",
61
+ base_config.merge(config[role].is_a?(Array) ? config[role].first : config[role])
62
+ )
41
63
  end
42
64
  end
43
65
 
44
- db_configs << environment_url_config(default_env, 'primary', {}) unless db_configs.find(&:for_current_env?)
66
+ db_configs << environment_url_config(default_env, "primary", {}) unless db_configs.find(&:for_current_env?)
45
67
 
46
68
  merge_db_environment_variables(default_env, db_configs.compact)
47
69
  end