switchman 3.4.2 → 3.6.7

Sign up to get free protection for your applications and to get access to all the features.
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