switchman 4.1.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 61591d46b119d4e93de2453131f6ec149238ece00df45ddabaac44a4fa67449c
4
- data.tar.gz: b055255214698f518056b88b1ba311243412b8a7b6757b239c4eb1b6caab7bf8
3
+ metadata.gz: cfb4e89f881628a5d90c8f309ddf587fdc5bfebe5bc7985c79fea9dd06e7168c
4
+ data.tar.gz: baa3202cd9fb68347d71d43adecdbe3124fe3f4f604558f92546e8fa5c5b4773
5
5
  SHA512:
6
- metadata.gz: a5c9b984bb1004b66269a070c39c20a8dab48b61f648716ec63e1144123cea232f42fdc670d923c85339e89ed6fca3bb93b28cf64560401b0b94ded01e0d96f9
7
- data.tar.gz: 0ee98392b52fce11f7b6ec11badaea968a46b984f459e5ed00c047afa82adaf726b4a37f1e24c437a36713c849802bd3b37de6e3df7270aa31e272ee77ce2d39
6
+ metadata.gz: c351d3ff898e668ac7a8c8ab40e9cf2249f428a2d2bbc9a16573334e1599cb9bcac20a47a3534e70b7900fd76ca55f6f4a8625c9dc7c8bb3ef1b4ade95a17ff3
7
+ data.tar.gz: e3205573c3bbf8d8bf8a1e87beaaaed1adc2dba268f53ec1bac8c870f29f51ecdce923028eaa8528cffb9250965fc7b72d8d2dce4882c63edb0ad0d66d624a57
@@ -27,12 +27,6 @@ module Switchman
27
27
  quote_table_name(name)
28
28
  end
29
29
 
30
- if ::Rails.version < "7.1"
31
- def schema_migration
32
- ::ActiveRecord::SchemaMigration
33
- end
34
- end
35
-
36
30
  protected
37
31
 
38
32
  def log(...)
@@ -23,7 +23,7 @@ module Switchman
23
23
  end
24
24
 
25
25
  module CollectionAssociation
26
- def find_target
26
+ def find_target(async: false)
27
27
  shards = if reflection.options[:multishard] && owner.respond_to?(:associated_shards)
28
28
  owner.associated_shards
29
29
  else
@@ -38,6 +38,8 @@ module Switchman
38
38
  # otherwise, the super call will set the shard_value to the object, causing it to iterate too many times
39
39
  # over the associated shards
40
40
  scope.shard(Shard.current(scope.klass.connection_class_for_self), :association).to_a
41
+ elsif ::Rails.version < "8.0"
42
+ super()
41
43
  else
42
44
  super
43
45
  end
@@ -251,78 +253,60 @@ module Switchman
251
253
  self.shard_source_value = :association
252
254
  end
253
255
 
254
- def shard(*args)
255
- scope.shard(*args)
256
+ def shard(*)
257
+ scope.shard(*)
256
258
  end
257
259
  end
258
260
 
259
261
  module AutosaveAssociation
260
- if ::Rails.version < "7.1"
261
- def association_foreign_key_changed?(reflection, record, key)
262
- return false if reflection.through_reflection?
262
+ def association_foreign_key_changed?(reflection, record, key)
263
+ return false if reflection.through_reflection?
263
264
 
264
- # have to use send instead of _read_attribute because sharding
265
- record.has_attribute?(reflection.foreign_key) && record.send(reflection.foreign_key) != key
266
- end
267
-
268
- def save_belongs_to_association(reflection)
269
- # this seems counter-intuitive, but the autosave code will assign to attribute bypassing switchman,
270
- # after reading the id attribute _without_ bypassing switchman. So we need Shard.current for the
271
- # category of the associated record to match Shard.current for the category of self
272
- shard.activate(connection_class_for_self_for_reflection(reflection)) { super }
273
- end
274
- else
275
- def association_foreign_key_changed?(reflection, record, key)
276
- return false if reflection.through_reflection?
265
+ foreign_key = Array(reflection.foreign_key)
266
+ return false unless foreign_key.all? { |k| record._has_attribute?(k) }
277
267
 
278
- foreign_key = Array(reflection.foreign_key)
279
- return false unless foreign_key.all? { |k| record._has_attribute?(k) }
268
+ # have to use send instead of _read_attribute because sharding
269
+ foreign_key.map { |k| record.send(k) } != Array(key)
270
+ end
280
271
 
281
- # have to use send instead of _read_attribute because sharding
282
- foreign_key.map { |k| record.send(k) } != Array(key)
283
- end
272
+ def save_belongs_to_association(reflection)
273
+ association = association_instance_get(reflection.name)
274
+ return unless association&.loaded? && !association.stale_target?
284
275
 
285
- def save_belongs_to_association(reflection)
286
- association = association_instance_get(reflection.name)
287
- return unless association&.loaded? && !association.stale_target?
276
+ record = association.load_target
277
+ return unless record && !record.destroyed?
288
278
 
289
- record = association.load_target
290
- return unless record && !record.destroyed?
279
+ autosave = reflection.options[:autosave]
291
280
 
292
- autosave = reflection.options[:autosave]
281
+ if autosave && record.marked_for_destruction?
282
+ foreign_key = Array(reflection.foreign_key)
283
+ foreign_key.each { |key| self[key] = nil }
284
+ record.destroy
285
+ elsif autosave != false
286
+ saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
293
287
 
294
- if autosave && record.marked_for_destruction?
288
+ if association.updated?
289
+ primary_key = Array(compute_primary_key(reflection, record)).map(&:to_s)
295
290
  foreign_key = Array(reflection.foreign_key)
296
- foreign_key.each { |key| self[key] = nil }
297
- record.destroy
298
- elsif autosave != false
299
- if record.new_record? || (autosave && record.changed_for_autosave?)
300
- saved = record.save(validate: !autosave)
301
- end
302
291
 
303
- if association.updated?
304
- primary_key = Array(compute_primary_key(reflection, record)).map(&:to_s)
305
- foreign_key = Array(reflection.foreign_key)
306
-
307
- primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
308
- primary_key_foreign_key_pairs.each do |pk, fk|
309
- # Notable change: add relative_id_for here
310
- association_id = if record.class.sharded_column?(pk)
311
- Shard.relative_id_for(
312
- record._read_attribute(pk),
313
- record.shard,
314
- shard
315
- )
316
- else
317
- record._read_attribute(pk)
318
- end
319
- self[fk] = association_id unless self[fk] == association_id
320
- end
321
- association.loaded!
292
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
293
+ primary_key_foreign_key_pairs.each do |pk, fk|
294
+ # Notable change: add relative_id_for here
295
+ association_id = if record.class.sharded_column?(pk)
296
+ Shard.relative_id_for(
297
+ record._read_attribute(pk),
298
+ record.shard,
299
+ shard
300
+ )
301
+ else
302
+ record._read_attribute(pk)
303
+ end
304
+ self[fk] = association_id unless self[fk] == association_id
322
305
  end
323
-
324
- saved if autosave
306
+ association.loaded!
325
307
  end
308
+
309
+ saved if autosave
326
310
  end
327
311
  end
328
312
  end
@@ -55,13 +55,13 @@ module Switchman
55
55
  raise if connection.open_transactions.positive?
56
56
  end
57
57
 
58
- def define_cached_method(owner, name, namespace:, as:, &block)
58
+ def define_cached_method(owner, name, namespace:, as:, &)
59
59
  if ::Rails.version < "7.1.4"
60
60
  # https://github.com/rails/rails/commit/a2a12fc2e3f4e6d06f81d4c74c88f8e6b3369ee6#diff-5b59ece6d9396b596f06271cec0ea726e3360911383511c49b1a66f454bfc2b6L30
61
61
  # These arguments were effectively swapped in Rails 7.1.4, so previous versions need them reversed
62
- owner.define_cached_method(as, namespace:, as: name, &block)
62
+ owner.define_cached_method(as, namespace:, as: name, &)
63
63
  else
64
- owner.define_cached_method(name, namespace:, as:, &block)
64
+ owner.define_cached_method(name, namespace:, as:, &)
65
65
  end
66
66
  end
67
67
 
@@ -53,11 +53,7 @@ module Switchman
53
53
  end
54
54
 
55
55
  def clear_query_caches_for_current_thread
56
- pools = if ::Rails.version < "7.1"
57
- ::ActiveRecord::Base.connection_handler.connection_pool_list
58
- else
59
- ::ActiveRecord::Base.connection_handler.connection_pool_list(:all)
60
- end
56
+ pools = ::ActiveRecord::Base.connection_handler.connection_pool_list(:all)
61
57
  pools.each do |pool|
62
58
  if ::Rails.version < "7.2"
63
59
  pool.connection(switch_shard: false).clear_query_cache if pool.active_connection?
@@ -145,7 +141,7 @@ module Switchman
145
141
  klass.singleton_class.prepend(ClassMethods)
146
142
  klass.singleton_class.prepend(Switchman::ActiveRecord::Relation::InsertUpsertAll) if ::Rails.version < "7.2"
147
143
  klass.scope :non_shadow, lambda { |key = primary_key|
148
- where(key => (QueryMethods::NonTransposingValue.new(0)..
144
+ where(key => (QueryMethods::NonTransposingValue.new(0)...
149
145
  QueryMethods::NonTransposingValue.new(Shard::IDS_PER_SHARD)))
150
146
  }
151
147
  klass.scope :shadow, lambda { |key = primary_key|
@@ -95,7 +95,7 @@ module Switchman
95
95
  opts[:group_columns].each do |aliaz, _type, group_column_name|
96
96
  if opts[:associated] && (aliaz == opts[:group_aliases].first)
97
97
  row[aliaz] = key_records[Shard.relative_id_for(row[aliaz], shard, target_shard)]
98
- elsif group_column_name && @klass.sharded_column?(group_column_name)
98
+ elsif group_column_name && klass.sharded_column?(group_column_name)
99
99
  row[aliaz] = Shard.relative_id_for(row[aliaz], shard, target_shard)
100
100
  end
101
101
  end
@@ -106,41 +106,39 @@ 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?
109
+ def ids
110
+ return super unless klass.sharded_primary_key?
112
111
 
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
112
+ if loaded?
113
+ result = records.map do |record|
114
+ Shard.relative_id_for(record._read_attribute(primary_key),
115
+ record.shard,
116
+ Shard.current(klass.connection_class_for_self))
120
117
  end
118
+ return @async ? Promise::Complete.new(result) : result
119
+ end
121
120
 
122
- if has_include?(primary_key)
123
- relation = apply_join_dependency.group(primary_key)
124
- return relation.ids
125
- end
121
+ if has_include?(primary_key)
122
+ relation = apply_join_dependency.group(primary_key)
123
+ return relation.ids
124
+ end
126
125
 
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
126
+ columns = arel_columns([primary_key])
127
+ base_shard = Shard.current(klass.connection_class_for_self)
128
+ activate do |r|
129
+ relation = r.spawn
130
+ relation.select_values = columns
131
+
132
+ result = if relation.where_clause.contradiction?
133
+ ::ActiveRecord::Result.empty
134
+ else
135
+ skip_query_cache_if_necessary do
136
+ klass.connection.select_all(relation, "#{klass.name} Ids", async: @async)
139
137
  end
138
+ end
140
139
 
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
140
+ result.then do |res|
141
+ type_cast_pluck_values(res, columns).map { |id| Shard.relative_id_for(id, Shard.current, base_shard) }
144
142
  end
145
143
  end
146
144
  end
@@ -161,10 +159,7 @@ module Switchman
161
159
  def grouped_calculation_options(operation, column_name, distinct)
162
160
  opts = { operation:, column_name:, distinct: }
163
161
 
164
- # Rails 7.0.5
165
- if defined?(::ActiveRecord::Calculations::ColumnAliasTracker)
166
- column_alias_tracker = ::ActiveRecord::Calculations::ColumnAliasTracker.new(connection)
167
- end
162
+ column_alias_tracker = ::ActiveRecord::Calculations::ColumnAliasTracker.new(connection)
168
163
 
169
164
  opts[:aggregate_alias] = aggregate_alias_for(operation, column_name, column_alias_tracker)
170
165
  group_attrs = group_values
@@ -179,11 +174,7 @@ module Switchman
179
174
 
180
175
  group_aliases = group_fields.map do |field|
181
176
  field = connection.visitor.compile(field) if ::Arel.arel_node?(field)
182
- if column_alias_tracker
183
- column_alias_tracker.alias_for(field.to_s.downcase)
184
- else
185
- column_alias_for(field.to_s.downcase)
186
- end
177
+ column_alias_tracker.alias_for(field.to_s.downcase)
187
178
  end
188
179
  group_columns = group_aliases.zip(group_fields).map do |aliaz, field|
189
180
  [aliaz, type_for(field), column_name_for(field)]
@@ -202,10 +193,8 @@ module Switchman
202
193
  "count_all"
203
194
  elsif operation == "average"
204
195
  "average"
205
- elsif column_alias_tracker
206
- column_alias_tracker.alias_for("#{operation} #{column_name}")
207
196
  else
208
- column_alias_for("#{operation} #{column_name}")
197
+ column_alias_tracker.alias_for("#{operation} #{column_name}")
209
198
  end
210
199
  end
211
200
 
@@ -3,25 +3,6 @@
3
3
  module Switchman
4
4
  module ActiveRecord
5
5
  module ConnectionPool
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
10
-
11
- self.schema_cache
12
- end
13
-
14
- # rubocop:disable Naming/AccessorMethodName -- override method
15
- def set_schema_cache(cache)
16
- schema_cache = get_schema_cache(cache.connection)
17
-
18
- cache.instance_variables.each do |x|
19
- schema_cache.instance_variable_set(x, cache.instance_variable_get(x))
20
- end
21
- end
22
- # rubocop:enable Naming/AccessorMethodName -- override method
23
- end
24
-
25
6
  def default_schema
26
7
  connection_method = (::Rails.version < "7.2") ? :connection : :lease_connection
27
8
  send(connection_method) unless @schemas
@@ -89,7 +70,11 @@ module Switchman
89
70
  private
90
71
 
91
72
  def current_shard
92
- connection_class.current_switchman_shard
73
+ if ::Rails.version < "8.0"
74
+ connection_class.current_switchman_shard
75
+ else
76
+ connection_descriptor.name.constantize.current_switchman_shard
77
+ end
93
78
  end
94
79
 
95
80
  def tls_key
@@ -7,26 +7,14 @@ 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
- 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
10
+ def configs_for(include_hidden: false, name: nil, **)
11
+ res = super
12
+ if name && !include_hidden
13
+ return nil unless name.end_with?("primary")
14
+ elsif !include_hidden
15
+ return res.select { |config| config.name.end_with?("primary") }
29
16
  end
17
+ res
30
18
  end
31
19
 
32
20
  private
@@ -42,56 +42,32 @@ module Switchman
42
42
  primary_shard.activate { super }
43
43
  end
44
44
 
45
- if ::Rails.version < "7.1"
46
- def exists?(conditions = :none)
47
- conditions = conditions.id if ::ActiveRecord::Base === conditions
48
- return false unless conditions
49
-
50
- relation = apply_join_dependency(eager_loading: false)
51
- return false if ::ActiveRecord::NullRelation === relation
52
-
53
- relation = relation.except(:select, :order).select("1 AS one").limit(1)
54
-
55
- case conditions
56
- when Array, Hash
57
- relation = relation.where(conditions)
58
- else
59
- relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
60
- end
61
-
62
- relation.activate do |shard_rel|
63
- return true if connection.select_value(shard_rel.arel, "#{name} Exists")
64
- end
65
- false
45
+ def exists?(conditions = :none)
46
+ return false if @none
47
+
48
+ if Base === conditions
49
+ raise ArgumentError, <<-TEXT.squish
50
+ You are passing an instance of ActiveRecord::Base to `exists?`.
51
+ Please pass the id of the object by calling `.id`.
52
+ TEXT
66
53
  end
67
- else
68
- def exists?(conditions = :none)
69
- return false if @none
70
-
71
- if Base === conditions
72
- raise ArgumentError, <<-TEXT.squish
73
- You are passing an instance of ActiveRecord::Base to `exists?`.
74
- Please pass the id of the object by calling `.id`.
75
- TEXT
76
- end
77
54
 
78
- return false if !conditions || limit_value == 0 # rubocop:disable Style/NumericPredicate
55
+ return false if !conditions || limit_value == 0 # rubocop:disable Style/NumericPredicate
79
56
 
80
- if eager_loading?
81
- relation = apply_join_dependency(eager_loading: false)
82
- return relation.exists?(conditions)
83
- end
57
+ if eager_loading?
58
+ relation = apply_join_dependency(eager_loading: false)
59
+ return relation.exists?(conditions)
60
+ end
84
61
 
85
- relation = construct_relation_for_exists(conditions)
86
- return false if relation.where_clause.contradiction?
62
+ relation = construct_relation_for_exists(conditions)
63
+ return false if relation.where_clause.contradiction?
87
64
 
88
- relation.activate do |shard_rel|
89
- return true if skip_query_cache_if_necessary do
90
- connection.select_rows(shard_rel.arel, "#{name} Exists?").size == 1
91
- end
65
+ relation.activate do |shard_rel|
66
+ return true if skip_query_cache_if_necessary do
67
+ connection.select_rows(shard_rel.arel, "#{name} Exists?").size == 1
92
68
  end
93
- false
94
69
  end
70
+ false
95
71
  end
96
72
  end
97
73
  end
@@ -5,11 +5,6 @@ module Switchman
5
5
  module LogSubscriber
6
6
  # sadly, have to completely replace this
7
7
  def sql(event)
8
- if ::Rails.version < "7.1"
9
- self.class.runtime += event.duration
10
- return unless logger.debug?
11
- end
12
-
13
8
  payload = event.payload
14
9
 
15
10
  return if ::ActiveRecord::LogSubscriber::IGNORE_PAYLOAD_NAMES.include?(payload[:name])
@@ -29,11 +24,7 @@ module Switchman
29
24
  end
30
25
 
31
26
  name = colorize_payload_name(name, payload[:name])
32
- sql = if ::Rails.version < "7.1"
33
- color(sql, sql_color(sql), true)
34
- else
35
- color(sql, sql_color(sql), bold: true)
36
- end
27
+ sql = color(sql, sql_color(sql), bold: true)
37
28
 
38
29
  debug " #{name} #{sql}#{binds}#{shard}"
39
30
  end
@@ -29,11 +29,7 @@ module Switchman
29
29
  db_name_hash = Zlib.crc32(Shard.current.name)
30
30
  shard_name_hash = ::ActiveRecord::Migrator::MIGRATOR_SALT * db_name_hash
31
31
  # Store in internalmetadata to allow other tools to be able to lock out migrations
32
- if ::Rails.version < "7.1"
33
- ::ActiveRecord::InternalMetadata[:migrator_advisory_lock_id] = shard_name_hash.to_s
34
- else
35
- @internal_metadata[:migrator_advisory_lock_id] = shard_name_hash.to_s
36
- end
32
+ @internal_metadata[:migrator_advisory_lock_id] = shard_name_hash.to_s
37
33
  shard_name_hash
38
34
  end
39
35
 
@@ -51,26 +47,14 @@ module Switchman
51
47
 
52
48
  module MigrationContext
53
49
  def migrate(...)
54
- connection = ::ActiveRecord::Base.connection
55
50
  schema_cache_holder = ::ActiveRecord::Base.connection_pool
56
- schema_cache_holder = schema_cache_holder.schema_reflection if ::Rails.version >= "7.1"
57
- previous_schema_cache = if ::Rails.version < "7.1"
58
- schema_cache_holder.get_schema_cache(connection)
59
- else
60
- schema_cache_holder.instance_variable_get(:@cache)
61
- end
62
-
63
- if ::Rails.version < "7.1"
64
- temporary_schema_cache = ::ActiveRecord::ConnectionAdapters::SchemaCache.new(connection)
51
+ schema_cache_holder = schema_cache_holder.schema_reflection
52
+ previous_schema_cache = schema_cache_holder.instance_variable_get(:@cache)
65
53
 
66
- reset_column_information
67
- schema_cache_holder.set_schema_cache(temporary_schema_cache)
68
- else
69
- schema_cache_holder.instance_variable_get(:@cache)
54
+ schema_cache_holder.instance_variable_get(:@cache)
70
55
 
71
- reset_column_information
72
- schema_cache_holder.clear!
73
- end
56
+ reset_column_information
57
+ schema_cache_holder.clear!
74
58
 
75
59
  begin
76
60
  super
@@ -5,67 +5,39 @@ module Switchman
5
5
  module QueryCache
6
6
  private
7
7
 
8
- if ::Rails.version < "7.1"
9
- def cache_sql(sql, name, binds)
10
- # have to include the shard id in the cache key because of switching dbs on the same connection
11
- sql = "#{shard.id}::#{sql}"
12
- @lock.synchronize do
13
- result =
14
- if query_cache[sql].key?(binds)
15
- args = {
16
- sql:,
17
- binds:,
18
- name:,
19
- connection_id: object_id,
20
- cached: true,
21
- type_casted_binds: -> { type_casted_binds(binds) }
22
- }
23
- ::ActiveSupport::Notifications.instrument(
24
- "sql.active_record",
25
- args
26
- )
27
- query_cache[sql][binds]
28
- else
29
- query_cache[sql][binds] = yield
30
- end
31
- result.dup
32
- end
33
- end
34
- else
35
- def cache_sql(sql, name, binds)
36
- # have to include the shard id in the cache key because of switching dbs on the same connection
37
- sql = "#{shard.id}::#{sql}"
38
- key = binds.empty? ? sql : [sql, binds]
39
- result = nil
40
- hit = false
8
+ def cache_sql(sql, name, binds)
9
+ # have to include the shard id in the cache key because of switching dbs on the same connection
10
+ sql = "#{shard.id}::#{sql}"
11
+ key = binds.empty? ? sql : [sql, binds]
12
+ result = nil
13
+ hit = false
41
14
 
42
- @lock.synchronize do
43
- if ::Rails.version < "7.2"
44
- if (result = @query_cache.delete(key))
45
- hit = true
46
- @query_cache[key] = result
47
- else
48
- result = @query_cache[key] = yield
49
- @query_cache.shift if @query_cache_max_size && @query_cache.size > @query_cache_max_size
50
- end
51
- else
15
+ @lock.synchronize do
16
+ if ::Rails.version < "7.2"
17
+ if (result = @query_cache.delete(key))
52
18
  hit = true
53
- result = @query_cache.compute_if_absent(key) do
54
- hit = false
55
- yield
56
- end
19
+ @query_cache[key] = result
20
+ else
21
+ result = @query_cache[key] = yield
22
+ @query_cache.shift if @query_cache_max_size && @query_cache.size > @query_cache_max_size
23
+ end
24
+ else
25
+ hit = true
26
+ result = @query_cache.compute_if_absent(key) do
27
+ hit = false
28
+ yield
57
29
  end
58
30
  end
31
+ end
59
32
 
60
- if hit
61
- ::ActiveSupport::Notifications.instrument(
62
- "sql.active_record",
63
- cache_notification_info(sql, name, binds)
64
- )
65
- end
66
-
67
- result.dup
33
+ if hit
34
+ ::ActiveSupport::Notifications.instrument(
35
+ "sql.active_record",
36
+ cache_notification_info(sql, name, binds)
37
+ )
68
38
  end
39
+
40
+ result.dup
69
41
  end
70
42
  end
71
43
  end
@@ -29,14 +29,14 @@ module Switchman
29
29
  params, connection = args
30
30
  klass = @klass
31
31
  target_shard = nil
32
- if (primary_index = bind_map.primary_value_index)
32
+ if (primary_index = @bind_map.primary_value_index)
33
33
  primary_value = params[primary_index]
34
34
  target_shard = Shard.local_id_for(primary_value)[1]
35
35
  end
36
36
  current_shard = Shard.current(klass.connection_class_for_self)
37
37
  target_shard ||= current_shard
38
38
 
39
- bind_values = bind_map.bind(params, current_shard, target_shard)
39
+ bind_values = @bind_map.bind(params, current_shard, target_shard)
40
40
 
41
41
  target_shard.activate(klass.connection_class_for_self) do
42
42
  sql = qualified_query_builder(target_shard, klass).sql_for(bind_values, connection)
@@ -15,17 +15,13 @@ module Switchman
15
15
  # Code adapted from the code in rails proper
16
16
  @connection_subscriber =
17
17
  ::ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
18
- spec_name = if ::Rails.version < "7.1"
19
- payload[:spec_name] if payload.key?(:spec_name)
20
- elsif payload.key?(:connection_name)
21
- payload[:connection_name]
22
- end
18
+ spec_name = (payload[:connection_name] if payload.key?(:connection_name))
23
19
  shard = payload[:shard] if payload.key?(:shard)
24
20
 
25
21
  if spec_name && !FORBIDDEN_DB_ENVS.include?(shard)
26
22
  begin
27
23
  connection = ::ActiveRecord::Base.connection_handler.retrieve_connection(spec_name, shard: shard)
28
- connection.connect! if ::Rails.version >= "7.1" # eagerly validate the connection
24
+ connection.connect! # eagerly validate the connection
29
25
  rescue ::ActiveRecord::ConnectionNotEstablished
30
26
  connection = nil
31
27
  end
@@ -38,31 +38,6 @@ module Switchman
38
38
  collector << quote_local_table_name(join_name) << "." << quote_column_name(o.name)
39
39
  end
40
40
 
41
- if ::Rails.version < "7.1"
42
- def visit_Arel_Nodes_HomogeneousIn(o, collector)
43
- collector.preparable = false
44
-
45
- collector << quote_local_table_name(o.table_name) << "." << quote_column_name(o.column_name)
46
-
47
- collector << if o.type == :in
48
- " IN ("
49
- else
50
- " NOT IN ("
51
- end
52
-
53
- values = o.casted_values
54
-
55
- if values.empty?
56
- collector << @connection.quote(nil)
57
- else
58
- collector.add_binds(values, o.proc_for_binds, &bind_block)
59
- end
60
-
61
- collector << ")"
62
- collector
63
- end
64
- end
65
-
66
41
  # rubocop:enable Naming/MethodName
67
42
  # rubocop:enable Naming/MethodParameterName
68
43
 
@@ -259,11 +259,7 @@ module Switchman
259
259
  unless schema == false
260
260
  shard.activate do
261
261
  ::ActiveRecord::Base.connection.transaction(requires_new: true) do
262
- if ::Rails.version < "7.1"
263
- ::ActiveRecord::Base.connection.migration_context.migrate
264
- else
265
- ::ActiveRecord::MigrationContext.new(::ActiveRecord::Migrator.migrations_paths).migrate
266
- end
262
+ ::ActiveRecord::MigrationContext.new(::ActiveRecord::Migrator.migrations_paths).migrate
267
263
  end
268
264
 
269
265
  ::ActiveRecord::Base.descendants.reject do |m|
@@ -4,8 +4,6 @@ module Switchman
4
4
  class Engine < ::Rails::Engine
5
5
  isolate_namespace Switchman
6
6
 
7
- # enable Rails 6.1 style connection handling
8
- config.active_record.legacy_connection_handling = false if ::Rails.version < "7.1"
9
7
  config.active_record.writing_role = :primary
10
8
 
11
9
  ::GuardRail.singleton_class.prepend(GuardRail::ClassMethods)
@@ -47,9 +45,7 @@ module Switchman
47
45
  ActiveRecord::Associations::Preloader::Association::LoaderRecords
48
46
  )
49
47
  ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::AbstractAdapter)
50
- unless ::Rails.version < "7.1"
51
- ::ActiveRecord::ConnectionAdapters::ConnectionHandler.prepend(ActiveRecord::ConnectionHandler)
52
- end
48
+ ::ActiveRecord::ConnectionAdapters::ConnectionHandler.prepend(ActiveRecord::ConnectionHandler)
53
49
  ::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ActiveRecord::ConnectionPool)
54
50
  ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::QueryCache)
55
51
  ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter)
@@ -216,11 +216,7 @@ module Switchman
216
216
  # clear connections prior to forking (no more queries will be executed in the parent,
217
217
  # and we want them gone so that we don't accidentally use them post-fork doing something
218
218
  # silly like dealloc'ing prepared statements)
219
- if ::Rails.version < "7.1"
220
- ::ActiveRecord::Base.clear_all_connections!(nil)
221
- else
222
- ::ActiveRecord::Base.connection_handler.clear_all_connections!(:all)
223
- end
219
+ ::ActiveRecord::Base.connection_handler.clear_all_connections!(:all)
224
220
 
225
221
  parent_process_name = sanitized_process_title
226
222
  ret = ::Parallel.map(scopes, in_processes: (scopes.length > 1) ? parallel : 0) do |server, subscope|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Switchman
4
- VERSION = "4.1.1"
4
+ VERSION = "4.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: switchman
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.1
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cody Cutrer
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2025-06-13 00:00:00.000000000 Z
13
+ date: 2025-08-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord
@@ -18,34 +18,34 @@ dependencies:
18
18
  requirements:
19
19
  - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: '7.0'
21
+ version: '7.1'
22
22
  - - "<"
23
23
  - !ruby/object:Gem::Version
24
- version: '7.3'
24
+ version: '8.1'
25
25
  type: :runtime
26
26
  prerelease: false
27
27
  version_requirements: !ruby/object:Gem::Requirement
28
28
  requirements:
29
29
  - - ">="
30
30
  - !ruby/object:Gem::Version
31
- version: '7.0'
31
+ version: '7.1'
32
32
  - - "<"
33
33
  - !ruby/object:Gem::Version
34
- version: '7.3'
34
+ version: '8.1'
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: guardrail
37
37
  requirement: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: 3.0.1
41
+ version: 3.1.0
42
42
  type: :runtime
43
43
  prerelease: false
44
44
  version_requirements: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: 3.0.1
48
+ version: 3.1.0
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: parallel
51
51
  requirement: !ruby/object:Gem::Requirement
@@ -66,20 +66,20 @@ dependencies:
66
66
  requirements:
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
- version: '7.0'
69
+ version: '7.1'
70
70
  - - "<"
71
71
  - !ruby/object:Gem::Version
72
- version: '7.3'
72
+ version: '8.1'
73
73
  type: :runtime
74
74
  prerelease: false
75
75
  version_requirements: !ruby/object:Gem::Requirement
76
76
  requirements:
77
77
  - - ">="
78
78
  - !ruby/object:Gem::Version
79
- version: '7.0'
79
+ version: '7.1'
80
80
  - - "<"
81
81
  - !ruby/object:Gem::Version
82
- version: '7.3'
82
+ version: '8.1'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: debug
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -312,14 +312,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
312
312
  requirements:
313
313
  - - ">="
314
314
  - !ruby/object:Gem::Version
315
- version: '3.1'
315
+ version: '3.2'
316
316
  required_rubygems_version: !ruby/object:Gem::Requirement
317
317
  requirements:
318
318
  - - ">="
319
319
  - !ruby/object:Gem::Version
320
320
  version: '0'
321
321
  requirements: []
322
- rubygems_version: 3.3.27
322
+ rubygems_version: 3.4.19
323
323
  signing_key:
324
324
  specification_version: 4
325
325
  summary: Rails sharding magic