switchman 4.3.5 → 4.3.6

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: 756e8de301a68ac11f33a55bf4686cfa5deb9ff15f40b8750a9ec9ade881e136
4
- data.tar.gz: 347370745f986e5b63bcc2c11c8855d5ca1a9c08d79e0f8b66d60552b0cbaa1b
3
+ metadata.gz: 2006f24bb61000cf22294395b7597c5551a56600b7c1b1fea0923f3d94e498e3
4
+ data.tar.gz: 30818e17d6547d86d46936e01c2df78118bbf992667cd1614c69bab9e173211c
5
5
  SHA512:
6
- metadata.gz: 6f50f01e9566f118a4bbc89a4aa30fabc8ed00d34fc020c84856ab5c027aa912b57462c7a5658f7b330eaed4308d121703266f441b9b1e451dcf4a2bf3e8ee3e
7
- data.tar.gz: 3146ef11b0fa04471c63c0bc74bb4344d15fc08a833bfbd878a5be82cfd024a2f6c2516bef2961e8032164f16db367c2b96aaf421032d2b7e10b4a3e599b00eb
6
+ metadata.gz: 110c91ddc723bde6ec5cbacbb992a67d87da0880fca81e22ff1b3c8d7852b0a53bc3024501c9eaae3c7a5f355615a5f6d880faf4fd5ace003adc148bcb14d3a8
7
+ data.tar.gz: '08a4458dcd9fa616e9079bd98f3609c42fa397941426c69725d517018c19b3e88dd952702c9bd7870112e8210fb85d94c8d6f706199aac1554c371e1f46b6f29'
@@ -2,7 +2,7 @@
2
2
 
3
3
  class CreateSwitchmanShards < ActiveRecord::Migration[4.2]
4
4
  def change
5
- create_table :switchman_shards do |t|
5
+ create_table :switchman_shards do |t| # rubocop:disable Rails/CreateTableWithTimestamps -- added in a subsequent migration
6
6
  t.string :name
7
7
  t.string :database_server_id
8
8
  t.boolean :default, default: false, null: false
@@ -2,8 +2,8 @@
2
2
 
3
3
  class AddDefaultShardIndex < ActiveRecord::Migration[4.2]
4
4
  def change
5
- Switchman::Shard.where(default: nil).update_all(default: false) if Switchman::Shard.current.default?
6
- change_column_default :switchman_shards, :default, false
5
+ Switchman::Shard.where(default: nil).update_all(default: false) if Switchman::Shard.current.default? # rubocop:disable Rails/SkipsModelValidations
6
+ change_column_default :switchman_shards, :default, from: nil, to: false
7
7
  change_column_null :switchman_shards, :default, false
8
8
  options = if connection.adapter_name == "PostgreSQL"
9
9
  { unique: true, where: '"default"' }
@@ -6,7 +6,7 @@ class AddTimestampsToShards < ActiveRecord::Migration[4.2]
6
6
  def change
7
7
  add_timestamps :switchman_shards, null: true, if_not_exists: true
8
8
  now = Time.now.utc
9
- Switchman::Shard.update_all(updated_at: now, created_at: now) if Switchman::Shard.current.default?
9
+ Switchman::Shard.update_all(updated_at: now, created_at: now) if Switchman::Shard.current.default? # rubocop:disable Rails/SkipsModelValidations
10
10
  change_column_null :switchman_shards, :updated_at, false
11
11
  change_column_null :switchman_shards, :created_at, false
12
12
 
@@ -22,7 +22,7 @@ module Switchman
22
22
 
23
23
  @instrumenter = Switchman::ShardedInstrumenter.new(@instrumenter, self) if ::Rails.version < "8.0"
24
24
 
25
- @last_query_at = Time.now
25
+ @last_query_at = Time.zone.now
26
26
  end
27
27
 
28
28
  if ::Rails.version >= "8.0"
@@ -40,7 +40,7 @@ module Switchman
40
40
  def log(...)
41
41
  super
42
42
  ensure
43
- @last_query_at = Time.now
43
+ @last_query_at = Time.zone.now
44
44
  end
45
45
  end
46
46
  end
@@ -27,6 +27,11 @@ module Switchman
27
27
 
28
28
  def define_attribute_methods
29
29
  result = super
30
+ # ActiveRecord::Core#init_internals calls this for every record it instantiates,
31
+ # and AR returns false once the methods have already been generated. Our class_evals
32
+ # below are just as redundant in that case, so don't pay for them per-record.
33
+ return result unless result
34
+
30
35
  # ensure that we're using the sharded attribute method
31
36
  # and not the silly one in AR::AttributeMethods::PrimaryKey
32
37
  return result unless sharded_column?(@primary_key)
@@ -190,7 +190,7 @@ module Switchman
190
190
  end
191
191
  end
192
192
  target_shard.activate do
193
- self.class.upsert_all([shadow_attrs], unique_by: self.class.primary_key)
193
+ self.class.upsert_all([shadow_attrs], unique_by: self.class.primary_key) # rubocop:disable Rails/SkipsModelValidations
194
194
  end
195
195
  end
196
196
 
@@ -205,7 +205,7 @@ module Switchman
205
205
  Array(target_shards).each do |target_shard|
206
206
  next if target_shard == shard
207
207
 
208
- target_shard.activate { self.class.where("id = ?", global_id).delete_all }
208
+ self.class.shard(target_shard).where(id: global_id).delete_all
209
209
  end
210
210
  end
211
211
 
@@ -83,7 +83,7 @@ module Switchman
83
83
  if opts[:association]
84
84
  key_ids = calculated_data.collect { |row| row[opts[:group_aliases].first] }
85
85
  key_records = opts[:association].klass.base_class.where(id: key_ids)
86
- key_records = key_records.to_h { |r| [Shard.relative_id_for(r, shard, target_shard), r] }
86
+ key_records = key_records.index_by { |r| Shard.relative_id_for(r, shard, target_shard) }
87
87
  end
88
88
 
89
89
  calculated_data.map do |row|
@@ -46,7 +46,7 @@ module Switchman
46
46
  return false if @none
47
47
 
48
48
  if Base === conditions
49
- raise ArgumentError, <<-TEXT.squish
49
+ raise ArgumentError, <<~TEXT.squish
50
50
  You are passing an instance of ActiveRecord::Base to `exists?`.
51
51
  Please pass the id of the object by calling `.id`.
52
52
  TEXT
@@ -4,13 +4,7 @@ module Switchman
4
4
  module ActiveRecord
5
5
  module PendingMigrationConnection
6
6
  module ClassMethods
7
- def current_role
8
- ::ActiveRecord::Base.current_role
9
- end
10
-
11
- def current_switchman_shard
12
- ::ActiveRecord::Base.current_switchman_shard
13
- end
7
+ delegate :current_role, :current_switchman_shard, to: :"::ActiveRecord::Base"
14
8
  end
15
9
  end
16
10
  end
@@ -94,9 +94,7 @@ module Switchman
94
94
  end
95
95
  end
96
96
 
97
- def quote_local_table_name(name)
98
- self.class.quote_local_table_name(name)
99
- end
97
+ delegate :quote_local_table_name, to: :class
100
98
 
101
99
  def quote_table_name(name)
102
100
  self.class.quote_table_name(name, shard: @use_local_table_name ? nil : shard)
@@ -5,9 +5,7 @@ module Switchman
5
5
  module QueryMethods
6
6
  # Use this class to prevent a value from getting transposed across shards
7
7
  class NonTransposingValue < SimpleDelegator
8
- def class
9
- __getobj__.class
10
- end
8
+ delegate :class, to: :__getobj__
11
9
 
12
10
  def is_a?(other)
13
11
  return true if other == NonTransposingValue
@@ -18,7 +18,7 @@ module Switchman
18
18
  spec_name = (payload[:connection_name] if payload.key?(:connection_name))
19
19
  shard = payload[:shard] if payload.key?(:shard)
20
20
 
21
- if spec_name && !FORBIDDEN_DB_ENVS.include?(shard)
21
+ if spec_name && FORBIDDEN_DB_ENVS.exclude?(shard)
22
22
  begin
23
23
  connection = ::ActiveRecord::Base.connection_handler.retrieve_connection(spec_name, shard:)
24
24
  connection.connect! # eagerly validate the connection
@@ -69,7 +69,7 @@ module Switchman
69
69
  shard = payload[:shard] if payload.key?(:shard)
70
70
 
71
71
  # INST: filter by FORBIDDEN_DB_ENVS
72
- if connection_name && !FORBIDDEN_DB_ENVS.include?(shard)
72
+ if connection_name && FORBIDDEN_DB_ENVS.exclude?(shard)
73
73
  pool = ::ActiveRecord::Base.connection_handler.retrieve_connection_pool(connection_name, shard:)
74
74
  if pool
75
75
  setup_shared_connection_pool
@@ -24,12 +24,11 @@ module Switchman
24
24
  store = super
25
25
  # must use the string name, otherwise it will try to auto-load the constant
26
26
  # and we don't want to require redis in this file (since it's not a hard dependency)
27
- # rubocop:disable Style/ClassEqualityComparison
27
+ # rubocop:disable-next Style/ClassEqualityComparison
28
28
  if store.class.name == "ActiveSupport::Cache::RedisCacheStore" &&
29
29
  !(::ActiveSupport::Cache::RedisCacheStore <= RedisCacheStore)
30
30
  ::ActiveSupport::Cache::RedisCacheStore.prepend(RedisCacheStore)
31
31
  end
32
- # rubocop:enable Style/ClassEqualityComparison
33
32
  store.options[:namespace] ||= -> { Shard.current.default? ? nil : "shard_#{Shard.current.id}" }
34
33
  store
35
34
  end
@@ -68,7 +68,7 @@ module Switchman
68
68
  end
69
69
 
70
70
  def database_servers
71
- if !@database_servers || @database_servers.empty?
71
+ if @database_servers.blank?
72
72
  @database_servers = {}.with_indifferent_access
73
73
  roles = []
74
74
  ::ActiveRecord::Base.configurations.configurations.each do |config|
@@ -48,15 +48,6 @@ module Switchman
48
48
  ::ActiveRecord::ConnectionAdapters::ConnectionHandler.prepend(ActiveRecord::ConnectionHandler)
49
49
  ::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ActiveRecord::ConnectionPool)
50
50
  ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::QueryCache)
51
- ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter)
52
- # https://github.com/rails/rails/commit/0016280f4fde55d96738887093dc333aae0d107b
53
- if ::Rails.version < "7.2"
54
- ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter::ClassMethods)
55
- else
56
- ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.singleton_class.prepend(
57
- ActiveRecord::PostgreSQLAdapter::ClassMethods
58
- )
59
- end
60
51
 
61
52
  ::ActiveRecord::DatabaseConfigurations.prepend(ActiveRecord::DatabaseConfigurations)
62
53
  ::ActiveRecord::DatabaseConfigurations::DatabaseConfig.prepend(
@@ -93,8 +84,6 @@ module Switchman
93
84
 
94
85
  ::ActiveRecord::Tasks::DatabaseTasks.singleton_class.prepend(ActiveRecord::Tasks::DatabaseTasks)
95
86
 
96
- ::ActiveRecord::TestFixtures.prepend(ActiveRecord::TestFixtures)
97
-
98
87
  ::ActiveRecord::TypeCaster::Map.include(ActiveRecord::TypeCaster::Map)
99
88
  ::ActiveRecord::TypeCaster::Connection.include(ActiveRecord::TypeCaster::Connection)
100
89
 
@@ -107,6 +96,19 @@ module Switchman
107
96
 
108
97
  ::ActiveRecord::ConnectionAdapters::TableDefinition.prepend(ActiveRecord::TableDefinition)
109
98
  end
99
+
100
+ ::ActiveSupport.on_load(:active_record_postgresqladapter) do
101
+ prepend ActiveRecord::PostgreSQLAdapter
102
+
103
+ # https://github.com/rails/rails/commit/0016280f4fde55d96738887093dc333aae0d107b
104
+ if ::Rails.version < "7.2"
105
+ prepend(ActiveRecord::PostgreSQLAdapter::ClassMethods)
106
+ else
107
+ singleton_class.prepend(ActiveRecord::PostgreSQLAdapter::ClassMethods)
108
+ end
109
+ end
110
+
111
+ ::ActiveSupport.on_load(:active_record_fixtures) { prepend ActiveRecord::TestFixtures }
110
112
  # Ensure that ActiveRecord::Base is always loaded before any app-level
111
113
  # initializers can go try to load Switchman::Shard or we get a loop
112
114
  ::ActiveRecord::Base
@@ -126,7 +128,7 @@ module Switchman
126
128
  # initialize_cache initializer, but for each value in the map, rather
127
129
  # than just Rails.cache. if config.cache_store is a flat value, uses it
128
130
  # to fill just the Rails.env entry in the cache map.
129
- unless Switchman.config[:cache_map].present?
131
+ if Switchman.config[:cache_map].blank?
130
132
  cache_store_config = ::Rails.configuration.cache_store
131
133
  cache_store_config = { ::Rails.env => cache_store_config } unless cache_store_config.is_a?(Hash)
132
134
 
@@ -158,7 +160,7 @@ module Switchman
158
160
  ::Rails.singleton_class.prepend(Rails::ClassMethods)
159
161
 
160
162
  ::ActiveSupport.on_load(:action_controller) do
161
- ::ActionController::Base.include(ActionController::Caching)
163
+ include ActionController::Caching
162
164
  end
163
165
  end
164
166
  end
@@ -20,9 +20,7 @@ module Switchman
20
20
  @wrapper = wrapper
21
21
  end
22
22
 
23
- def exception
24
- @wrapper.exception
25
- end
23
+ delegate :exception, to: :@wrapper
26
24
  end
27
25
 
28
26
  class UndumpableResult
@@ -17,6 +17,7 @@ module Switchman
17
17
  klass.parent_groups.any? { |group| group.included_modules.include?(self) }
18
18
  end
19
19
 
20
+ # rubocop:disable-next Rails/Output
20
21
  def self.included(klass)
21
22
  # our before handlers have already been configured from a parent group; don't add them again
22
23
  parent_group = klass.parent_groups[1]
@@ -85,7 +86,7 @@ module Switchman
85
86
  @@shard2.destroy
86
87
  end
87
88
  @@shard2.database_server.destroy
88
- exit status if status
89
+ exit status if status # rubocop:disable Rails/Exit
89
90
  end
90
91
  end
91
92
 
@@ -5,10 +5,8 @@ module Switchman
5
5
  # ten trillion possible ids per shard. yup.
6
6
  IDS_PER_SHARD = 10_000_000_000_000
7
7
 
8
- # rubocop:disable Style/SymbolProc -- transforming to a lambda produces "no receiver given"
9
8
  # only allow one default
10
9
  validates_uniqueness_of :default, if: ->(s) { s.default? }
11
- # rubocop:enable Style/SymbolProc
12
10
 
13
11
  after_save :clear_cache
14
12
  after_destroy :clear_cache
@@ -79,7 +77,7 @@ module Switchman
79
77
 
80
78
  # Now find the actual record, if it exists
81
79
  @default = begin
82
- find_cached("default_shard") { Shard.where(default: true).take } || default
80
+ find_cached("default_shard") { Shard.find_by(default: true) } || default
83
81
  rescue
84
82
  default
85
83
  end
@@ -215,8 +213,8 @@ module Switchman
215
213
  # nothing to do
216
214
  return if database_servers.none?
217
215
 
218
- scopes = database_servers.to_h do |server|
219
- [server, scope.merge(server.shards)]
216
+ scopes = database_servers.index_with do |server|
217
+ scope.merge(server.shards)
220
218
  end
221
219
  else
222
220
  scopes = scope.group_by(&:database_server)
@@ -665,9 +663,7 @@ module Switchman
665
663
  end
666
664
 
667
665
  # skip global_id.hash
668
- def hash
669
- id.hash
670
- end
666
+ delegate :hash, to: :id
671
667
 
672
668
  def destroy
673
669
  raise("Cannot destroy the default shard") if default?
@@ -696,8 +692,8 @@ module Switchman
696
692
  if classes.empty?
697
693
  { ::ActiveRecord::Base => self }
698
694
  else
699
- classes.to_h do |klass|
700
- [klass, self]
695
+ classes.index_with do
696
+ self
701
697
  end
702
698
  end
703
699
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Switchman
4
- class UnshardedRecord < ::ActiveRecord::Base
4
+ class UnshardedRecord < ::ActiveRecord::Base # rubocop:disable Rails/ApplicationRecord
5
5
  self.abstract_class = true
6
6
  end
7
7
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Switchman
4
- VERSION = "4.3.5"
4
+ VERSION = "4.3.6"
5
5
  end
data/lib/switchman.rb CHANGED
@@ -39,7 +39,7 @@ module Switchman
39
39
  def foreign_key_check(name, type, limit: nil)
40
40
  return unless name.to_s.end_with?("_id") && type.to_s == "integer" && limit.to_i < 8
41
41
 
42
- puts <<~TEXT.squish
42
+ warn <<~TEXT.squish
43
43
  WARNING: All foreign keys need to be 8-byte integers.
44
44
  #{name} looks like a foreign key.
45
45
  If so, please add the option: `:limit => 8`
@@ -243,7 +243,7 @@ module Switchman
243
243
  scope = scope.where(positive_query, *conditions) unless positive_queries.empty?
244
244
 
245
245
  scope = scope.where("NOT (#{negative_ranges.join(" OR")})") unless negative_ranges.empty?
246
- scope = scope.where("id NOT IN (?)", negative_shard_ids) unless negative_shard_ids.empty?
246
+ scope = scope.where.not(id: negative_shard_ids) unless negative_shard_ids.empty?
247
247
  scope
248
248
  end
249
249
 
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.3.5
4
+ version: 4.3.6
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: 2026-09-01 00:00:00.000000000 Z
13
+ date: 2026-09-10 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activerecord