switchman 3.4.2 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) 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/associations.rb +16 -5
  6. data/lib/switchman/active_record/attribute_methods.rb +67 -22
  7. data/lib/switchman/active_record/base.rb +32 -12
  8. data/lib/switchman/active_record/calculations.rb +37 -33
  9. data/lib/switchman/active_record/connection_pool.rb +4 -2
  10. data/lib/switchman/active_record/database_configurations.rb +12 -7
  11. data/lib/switchman/active_record/finder_methods.rb +1 -1
  12. data/lib/switchman/active_record/log_subscriber.rb +2 -2
  13. data/lib/switchman/active_record/migration.rb +4 -2
  14. data/lib/switchman/active_record/postgresql_adapter.rb +11 -10
  15. data/lib/switchman/active_record/query_cache.rb +1 -1
  16. data/lib/switchman/active_record/query_methods.rb +72 -26
  17. data/lib/switchman/active_record/relation.rb +13 -7
  18. data/lib/switchman/active_record/spawn_methods.rb +2 -2
  19. data/lib/switchman/active_record/statement_cache.rb +2 -2
  20. data/lib/switchman/active_record/tasks/database_tasks.rb +1 -1
  21. data/lib/switchman/active_record/test_fixtures.rb +19 -16
  22. data/lib/switchman/active_support/cache.rb +4 -1
  23. data/lib/switchman/arel.rb +6 -6
  24. data/lib/switchman/call_super.rb +1 -1
  25. data/lib/switchman/database_server.rb +20 -16
  26. data/lib/switchman/default_shard.rb +3 -3
  27. data/lib/switchman/engine.rb +33 -18
  28. data/lib/switchman/environment.rb +2 -2
  29. data/lib/switchman/errors.rb +4 -1
  30. data/lib/switchman/guard_rail/relation.rb +1 -1
  31. data/lib/switchman/parallel.rb +1 -1
  32. data/lib/switchman/r_spec_helper.rb +10 -10
  33. data/lib/switchman/shard.rb +30 -23
  34. data/lib/switchman/sharded_instrumenter.rb +5 -1
  35. data/lib/switchman/test_helper.rb +1 -1
  36. data/lib/switchman/version.rb +1 -1
  37. data/lib/switchman.rb +10 -4
  38. data/lib/tasks/switchman.rake +40 -37
  39. metadata +9 -9
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'securerandom'
3
+ require "securerandom"
4
4
 
5
5
  module Switchman
6
6
  class DatabaseServer
@@ -16,13 +16,13 @@ module Switchman
16
16
 
17
17
  def find(id_or_all)
18
18
  return all if id_or_all == :all
19
- return id_or_all.map { |id| database_servers[id || ::Rails.env] }.compact.uniq if id_or_all.is_a?(Array)
19
+ return id_or_all.filter_map { |id| database_servers[id || ::Rails.env] }.uniq if id_or_all.is_a?(Array)
20
20
 
21
21
  database_servers[id_or_all || ::Rails.env]
22
22
  end
23
23
 
24
24
  def create(settings = {})
25
- raise 'database servers should be set up in database.yml' unless ::Rails.env.test?
25
+ raise "database servers should be set up in database.yml" unless ::Rails.env.test?
26
26
 
27
27
  id = settings[:id]
28
28
  unless id
@@ -64,8 +64,8 @@ module Switchman
64
64
  @database_servers = {}.with_indifferent_access
65
65
  roles = []
66
66
  ::ActiveRecord::Base.configurations.configurations.each do |config|
67
- if config.name.include?('/')
68
- name, role = config.name.split('/')
67
+ if config.name.include?("/")
68
+ name, role = config.name.split("/")
69
69
  else
70
70
  name, role = config.env_name, config.name
71
71
  end
@@ -110,8 +110,9 @@ module Switchman
110
110
  self.class.send(:database_servers).delete(id) if id
111
111
  Shard.sharded_models.each do |klass|
112
112
  self.class.all_roles.each do |role|
113
- klass.connection_handler.remove_connection_pool(klass.connection_specification_name, role: role,
114
- shard: id.to_sym)
113
+ klass.connection_handler.remove_connection_pool(klass.connection_specification_name,
114
+ role: role,
115
+ shard: id.to_sym)
115
116
  end
116
117
  end
117
118
  end
@@ -142,11 +143,13 @@ module Switchman
142
143
  # value of GuardRail.environment)
143
144
  def guard!(environment = :secondary)
144
145
  DatabaseServer.send(:reference_role, environment)
145
- ::ActiveRecord::Base.connected_to_stack << { shard_roles: { id.to_sym => environment }, klasses: [::ActiveRecord::Base] }
146
+ ::ActiveRecord::Base.connected_to_stack << { shard_roles: { id.to_sym => environment },
147
+ klasses: [::ActiveRecord::Base] }
146
148
  end
147
149
 
148
150
  def unguard!
149
- ::ActiveRecord::Base.connected_to_stack << { shard_roles: { id.to_sym => :_switchman_inherit }, klasses: [::ActiveRecord::Base] }
151
+ ::ActiveRecord::Base.connected_to_stack << { shard_roles: { id.to_sym => :_switchman_inherit },
152
+ klasses: [::ActiveRecord::Base] }
150
153
  end
151
154
 
152
155
  def unguard
@@ -162,7 +165,7 @@ module Switchman
162
165
 
163
166
  def shards
164
167
  if id == ::Rails.env
165
- Shard.where('database_server_id IS NULL OR database_server_id=?', id)
168
+ Shard.where("database_server_id IS NULL OR database_server_id=?", id)
166
169
  else
167
170
  Shard.where(database_server_id: id)
168
171
  end
@@ -187,7 +190,7 @@ module Switchman
187
190
  end
188
191
 
189
192
  id ||= begin
190
- id_seq = Shard.connection.quote(Shard.connection.quote_table_name('switchman_shards_id_seq'))
193
+ id_seq = Shard.connection.quote(Shard.connection.quote_table_name("switchman_shards_id_seq"))
191
194
  next_id = Shard.connection.select_value("SELECT nextval(#{id_seq})")
192
195
  next_id.to_i
193
196
  end
@@ -204,17 +207,18 @@ module Switchman
204
207
  name: name,
205
208
  database_server_id: self.id)
206
209
  if create_statement
207
- if ::ActiveRecord::Base.connection.select_value("SELECT 1 FROM pg_namespace WHERE nspname=#{::ActiveRecord::Base.connection.quote(name)}")
210
+ if ::ActiveRecord::Base.connection.select_value(
211
+ "SELECT 1 FROM pg_namespace WHERE nspname=#{::ActiveRecord::Base.connection.quote(name)}"
212
+ )
208
213
  schema_already_existed = true
209
- raise 'This schema already exists; cannot overwrite'
214
+ raise "This schema already exists; cannot overwrite"
210
215
  end
211
216
  Array(create_statement.call).each do |stmt|
212
217
  ::ActiveRecord::Base.connection.execute(stmt)
213
218
  end
214
219
  end
215
- if config[:adapter] == 'postgresql'
216
- old_proc = ::ActiveRecord::Base.connection.raw_connection.set_notice_processor do
217
- end
220
+ if config[:adapter] == "postgresql"
221
+ old_proc = ::ActiveRecord::Base.connection.raw_connection.set_notice_processor {}
218
222
  end
219
223
  old_verbose = ::ActiveRecord::Migration.verbose
220
224
  ::ActiveRecord::Migration.verbose = false
@@ -3,9 +3,9 @@
3
3
  module Switchman
4
4
  class DefaultShard
5
5
  def id
6
- 'default'
6
+ "default"
7
7
  end
8
- alias cache_key id
8
+ alias_method :cache_key, :id
9
9
  def activate(*_classes)
10
10
  yield
11
11
  end
@@ -58,7 +58,7 @@ module Switchman
58
58
  end
59
59
 
60
60
  def _dump(_depth)
61
- ''
61
+ ""
62
62
  end
63
63
 
64
64
  def self._load(_str)
@@ -10,19 +10,23 @@ module Switchman
10
10
 
11
11
  ::GuardRail.singleton_class.prepend(GuardRail::ClassMethods)
12
12
 
13
- # after :initialize_dependency_mechanism to ensure autoloading is configured for any downstream initializers that care
14
- # In rails 7.0 we should be able to just use an explicit after on configuring the once autoloaders and not need to go monkey around with initializer order
15
- if ::Rails.version < '7.0'
16
- initialize_dependency_mechanism = ::Rails::Application::Bootstrap.initializers.find { |i| i.name == :initialize_dependency_mechanism }
13
+ # after :initialize_dependency_mechanism to ensure autoloading is
14
+ # configured for any downstream initializers that care. In rails 7.0 we
15
+ # should be able to just use an explicit after on configuring the once
16
+ # autoloaders and not need to go monkey around with initializer order
17
+ if ::Rails.version < "7.0"
18
+ initialize_dependency_mechanism = ::Rails::Application::Bootstrap.initializers.find do |i|
19
+ i.name == :initialize_dependency_mechanism
20
+ end
17
21
  initialize_dependency_mechanism.instance_variable_get(:@options)[:after] = :set_autoload_paths
18
22
  end
19
23
 
20
- initializer 'switchman.active_record_patch',
21
- before: 'active_record.initialize_database',
22
- after: (::Rails.version < '7.0' ? :initialize_dependency_mechanism : :setup_once_autoloader) do
24
+ initializer "switchman.active_record_patch",
25
+ before: "active_record.initialize_database",
26
+ after: ((::Rails.version < "7.0") ? :initialize_dependency_mechanism : :setup_once_autoloader) do
23
27
  ::ActiveSupport.on_load(:active_record) do
24
28
  # Switchman requires postgres, so just always load the pg adapter
25
- require 'active_record/connection_adapters/postgresql_adapter'
29
+ require "active_record/connection_adapters/postgresql_adapter"
26
30
 
27
31
  self.default_shard = ::Rails.env.to_sym
28
32
  self.default_role = :primary
@@ -50,14 +54,20 @@ module Switchman
50
54
  ::ActiveRecord::Associations::CollectionProxy.include(ActiveRecord::Associations::CollectionProxy)
51
55
 
52
56
  ::ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecord::Associations::Preloader::Association)
53
- ::ActiveRecord::Associations::Preloader::Association::LoaderRecords.prepend(ActiveRecord::Associations::Preloader::Association::LoaderRecords) unless ::Rails.version < '7.0'
57
+ unless ::Rails.version < "7.0"
58
+ ::ActiveRecord::Associations::Preloader::Association::LoaderRecords.prepend(
59
+ ActiveRecord::Associations::Preloader::Association::LoaderRecords
60
+ )
61
+ end
54
62
  ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::AbstractAdapter)
55
63
  ::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ActiveRecord::ConnectionPool)
56
64
  ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::QueryCache)
57
65
  ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter)
58
66
 
59
67
  ::ActiveRecord::DatabaseConfigurations.prepend(ActiveRecord::DatabaseConfigurations)
60
- ::ActiveRecord::DatabaseConfigurations::DatabaseConfig.prepend(ActiveRecord::DatabaseConfigurations::DatabaseConfig)
68
+ ::ActiveRecord::DatabaseConfigurations::DatabaseConfig.prepend(
69
+ ActiveRecord::DatabaseConfigurations::DatabaseConfig
70
+ )
61
71
 
62
72
  ::ActiveRecord::LogSubscriber.prepend(ActiveRecord::LogSubscriber)
63
73
  ::ActiveRecord::Migration.prepend(ActiveRecord::Migration)
@@ -77,8 +87,12 @@ module Switchman
77
87
  ::ActiveRecord::Relation.include(ActiveRecord::SpawnMethods)
78
88
  ::ActiveRecord::Relation.include(CallSuper)
79
89
 
80
- ::ActiveRecord::PredicateBuilder::AssociationQueryValue.prepend(ActiveRecord::PredicateBuilder::AssociationQueryValue)
81
- ::ActiveRecord::PredicateBuilder::PolymorphicArrayValue.prepend(ActiveRecord::PredicateBuilder::AssociationQueryValue)
90
+ ::ActiveRecord::PredicateBuilder::AssociationQueryValue.prepend(
91
+ ActiveRecord::PredicateBuilder::AssociationQueryValue
92
+ )
93
+ ::ActiveRecord::PredicateBuilder::PolymorphicArrayValue.prepend(
94
+ ActiveRecord::PredicateBuilder::AssociationQueryValue
95
+ )
82
96
 
83
97
  ::ActiveRecord::Tasks::DatabaseTasks.singleton_class.prepend(ActiveRecord::Tasks::DatabaseTasks)
84
98
 
@@ -96,17 +110,18 @@ module Switchman
96
110
 
97
111
  ::ActiveRecord::ConnectionAdapters::TableDefinition.prepend(ActiveRecord::TableDefinition)
98
112
  end
99
- # Ensure that ActiveRecord::Base is always loaded before any app-level initializers can go try to load Switchman::Shard or we get a loop
113
+ # Ensure that ActiveRecord::Base is always loaded before any app-level
114
+ # initializers can go try to load Switchman::Shard or we get a loop
100
115
  ::ActiveRecord::Base
101
116
  end
102
117
 
103
- initializer 'switchman.error_patch', after: 'active_record.initialize_database' do
118
+ initializer "switchman.error_patch", after: "active_record.initialize_database" do
104
119
  ::ActiveSupport.on_load(:active_record) do
105
120
  ::StandardError.include(StandardError)
106
121
  end
107
122
  end
108
123
 
109
- initializer 'switchman.initialize_cache', before: :initialize_cache, after: 'active_record.initialize_database' do
124
+ initializer "switchman.initialize_cache", before: :initialize_cache, after: "active_record.initialize_database" do
110
125
  ::ActiveSupport::Cache.singleton_class.prepend(ActiveSupport::Cache::ClassMethods)
111
126
 
112
127
  # if we haven't already setup our cache map out-of-band, set it up from
@@ -130,11 +145,11 @@ module Switchman
130
145
  Switchman.config[:cache_map][::Rails.env] = value
131
146
  end
132
147
 
133
- middlewares = Switchman.config[:cache_map].values.map do |store|
148
+ middlewares = Switchman.config[:cache_map].values.filter_map do |store|
134
149
  store.middleware if store.respond_to?(:middleware)
135
- end.compact.uniq
150
+ end.uniq
136
151
  middlewares.each do |middleware|
137
- config.middleware.insert_before('Rack::Runtime', middleware)
152
+ config.middleware.insert_before("Rack::Runtime", middleware)
138
153
  end
139
154
 
140
155
  # prevent :initialize_cache from trying to (or needing to) set
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'etc'
3
+ require "etc"
4
4
 
5
5
  module Switchman
6
6
  class Environment
7
- def self.cpu_count(nproc_bin = 'nproc')
7
+ def self.cpu_count(nproc_bin = "nproc")
8
8
  return Etc.nprocessors if Etc.respond_to?(:nprocessors)
9
9
 
10
10
  `#{nproc_bin}`.to_i
@@ -3,7 +3,10 @@
3
3
  module Switchman
4
4
  module Errors
5
5
  class ManuallyCreatedShadowRecordError < RuntimeError
6
- def initialize(msg = "It looks like you're trying to manually create a shadow record. Please use Switchman::ActiveRecord::Base#save_shadow_record instead.")
6
+ DEFAULT_MSG = "It looks like you're trying to manually create a shadow record. " \
7
+ "Please use Switchman::ActiveRecord::Base#save_shadow_record instead."
8
+
9
+ def initialize(msg = DEFAULT_MSG)
7
10
  super
8
11
  end
9
12
  end
@@ -13,7 +13,7 @@ module Switchman
13
13
  end
14
14
 
15
15
  %w[update_all delete_all].each do |method|
16
- arg_params = RUBY_VERSION <= '2.8' ? '*args' : '*args, **kwargs'
16
+ arg_params = (RUBY_VERSION <= "2.8") ? "*args" : "*args, **kwargs"
17
17
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
18
18
  def #{method}(#{arg_params})
19
19
  db = Shard.current(connection_class_for_self).database_server
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'parallel'
3
+ require "parallel"
4
4
 
5
5
  module Switchman
6
6
  module Parallel
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'switchman/test_helper'
3
+ require "switchman/test_helper"
4
4
 
5
5
  module Switchman
6
6
  # including this module in your specs will give you several shards to
@@ -34,9 +34,9 @@ module Switchman
34
34
  groups = group.class.descendant_filtered_examples.map(&:example_group).uniq
35
35
  next unless groups.any? { |descendant_group| RSpecHelper.included_in?(descendant_group) }
36
36
 
37
- puts 'Setting up sharding for all specs...'
37
+ puts "Setting up sharding for all specs..."
38
38
  Shard.delete_all
39
- Switchman.cache.delete('default_shard')
39
+ Switchman.cache.delete("default_shard")
40
40
 
41
41
  @@shard1, @@shard2 = TestHelper.recreate_persistent_test_shards
42
42
  @@default_shard = Shard.default
@@ -48,7 +48,7 @@ module Switchman
48
48
  @@shard1 = @@shard1.create_new_shard
49
49
  @@shard2 = @@shard2.create_new_shard
50
50
  rescue => e
51
- warn 'Sharding setup FAILED!:'
51
+ warn "Sharding setup FAILED!:"
52
52
  while e
53
53
  warn "\n#{e}\n"
54
54
  warn e.backtrace
@@ -66,9 +66,9 @@ module Switchman
66
66
  # we'll re-persist in the group's `before :all`; we don't want them to exist
67
67
  # in the db before then
68
68
  Shard.delete_all
69
- Switchman.cache.delete('default_shard')
69
+ Switchman.cache.delete("default_shard")
70
70
  Shard.default(reload: true)
71
- puts 'Done!'
71
+ puts "Done!"
72
72
 
73
73
  main_pid = Process.pid
74
74
  at_exit do
@@ -76,7 +76,7 @@ module Switchman
76
76
 
77
77
  # preserve rspec's exit status
78
78
  status = $!.is_a?(::SystemExit) ? $!.status : nil
79
- puts 'Tearing down sharding for all specs'
79
+ puts "Tearing down sharding for all specs"
80
80
  @@shard1.database_server.destroy unless @@shard1.database_server == Shard.default.database_server
81
81
  unless @@keep_the_shards
82
82
  @@shard1.drop_database
@@ -95,7 +95,7 @@ module Switchman
95
95
  dup = @@default_shard.dup
96
96
  dup.id = @@default_shard.id
97
97
  dup.save!
98
- Switchman.cache.delete('default_shard')
98
+ Switchman.cache.delete("default_shard")
99
99
  Shard.default(reload: true)
100
100
  dup = @@shard1.dup
101
101
  dup.id = @@shard1.id
@@ -107,7 +107,7 @@ module Switchman
107
107
  end
108
108
 
109
109
  klass.before do
110
- raise 'Sharding did not set up correctly' if @@sharding_failed
110
+ raise "Sharding did not set up correctly" if @@sharding_failed
111
111
 
112
112
  Shard.clear_cache
113
113
  if use_transactional_tests
@@ -132,7 +132,7 @@ module Switchman
132
132
  klass.after(:all) do
133
133
  # Don't truncate because that can create some fun cross-connection lock contention
134
134
  Shard.delete_all
135
- Switchman.cache.delete('default_shard')
135
+ Switchman.cache.delete("default_shard")
136
136
  Shard.default(reload: true)
137
137
  end
138
138
  end
@@ -36,13 +36,15 @@ module Switchman
36
36
 
37
37
  # Now find the actual record, if it exists
38
38
  @default = begin
39
- find_cached('default_shard') { Shard.where(default: true).take } || default
39
+ find_cached("default_shard") { Shard.where(default: true).take } || default
40
40
  rescue
41
41
  default
42
42
  end
43
43
 
44
44
  # make sure this is not erroneously cached
45
- @default.database_server.remove_instance_variable(:@primary_shard) if @default.database_server.instance_variable_defined?(:@primary_shard)
45
+ if @default.database_server.instance_variable_defined?(:@primary_shard)
46
+ @default.database_server.remove_instance_variable(:@primary_shard)
47
+ end
46
48
 
47
49
  # and finally, check for cached references to the default shard on the existing connection
48
50
  sharded_models.each do |klass|
@@ -75,28 +77,30 @@ module Switchman
75
77
  klass.current_switchman_shard != shard
76
78
 
77
79
  (activated_classes ||= []) << klass
78
- klass.connected_to_stack << { shard: shard.database_server.id.to_sym, klasses: [klass], switchman_shard: shard }
80
+ klass.connected_to_stack << { shard: shard.database_server.id.to_sym,
81
+ klasses: [klass],
82
+ switchman_shard: shard }
79
83
  end
80
84
  activated_classes
81
85
  end
82
86
 
83
87
  def active_shards
84
- sharded_models.map do |klass|
88
+ sharded_models.filter_map do |klass|
85
89
  [klass, current(klass)]
86
- end.compact.to_h
90
+ end.to_h
87
91
  end
88
92
 
89
93
  def lookup(id)
90
94
  id_i = id.to_i
91
- return current if id_i == current.id || id == 'self'
92
- return default if id_i == default.id || id.nil? || id == 'default'
95
+ return current if id_i == current.id || id == "self"
96
+ return default if id_i == default.id || id.nil? || id == "default"
93
97
 
94
98
  id = id_i
95
99
  raise ArgumentError if id.zero?
96
100
 
97
101
  unless cached_shards.key?(id)
98
102
  cached_shards[id] = Shard.default.activate do
99
- find_cached(['shard', id]) { find_by(id: id) }
103
+ find_cached(["shard", id]) { find_by(id: id) }
100
104
  end
101
105
  end
102
106
  cached_shards[id]
@@ -140,13 +144,15 @@ module Switchman
140
144
  parallel = 0 if parallel == false || parallel.nil?
141
145
 
142
146
  scope ||= Shard.all
143
- scope = scope.order(::Arel.sql('database_server_id IS NOT NULL, database_server_id, id')) if ::ActiveRecord::Relation === scope && scope.order_values.empty?
147
+ if ::ActiveRecord::Relation === scope && scope.order_values.empty?
148
+ scope = scope.order(::Arel.sql("database_server_id IS NOT NULL, database_server_id, id"))
149
+ end
144
150
 
145
151
  if parallel > 1
146
152
  if ::ActiveRecord::Relation === scope
147
153
  # still need a post-uniq, cause the default database server could be NULL or Rails.env in the db
148
- database_servers = scope.reorder('database_server_id').select(:database_server_id).distinct.
149
- map(&:database_server).compact.uniq
154
+ database_servers = scope.reorder("database_server_id").select(:database_server_id).distinct
155
+ .filter_map(&:database_server).uniq
150
156
  # nothing to do
151
157
  return if database_servers.count.zero?
152
158
 
@@ -163,14 +169,14 @@ module Switchman
163
169
  ::ActiveRecord::Base.clear_all_connections!
164
170
 
165
171
  parent_process_name = `ps -ocommand= -p#{Process.pid}`.slice(/#{$0}.*/)
166
- ret = ::Parallel.map(scopes, in_processes: scopes.length > 1 ? parallel : 0) do |server, subscope|
172
+ ret = ::Parallel.map(scopes, in_processes: (scopes.length > 1) ? parallel : 0) do |server, subscope|
167
173
  name = server.id
168
174
  last_description = name
169
175
 
170
176
  begin
171
177
  max_length = 128 - name.length - 3
172
178
  short_parent_name = parent_process_name[0..max_length] if max_length >= 0
173
- new_title = [short_parent_name, name].join(' ')
179
+ new_title = [short_parent_name, name].join(" ")
174
180
  Process.setproctitle(new_title)
175
181
  Switchman.config[:on_fork_proc]&.call
176
182
  with_each_shard(subscope, classes, exception: exception, output: :decorated) do
@@ -187,8 +193,9 @@ module Switchman
187
193
  unless errors.empty?
188
194
  raise errors.first.exception if errors.length == 1
189
195
 
196
+ errors_desc = errors.map(&:name).sort.join(", ")
190
197
  raise Errors::ParallelShardExecError,
191
- "The following database server(s) did not finish processing cleanly: #{errors.map(&:name).sort.join(', ')}",
198
+ "The following database server(s) did not finish processing cleanly: #{errors_desc}",
192
199
  cause: errors.first.exception
193
200
  end
194
201
 
@@ -467,7 +474,7 @@ module Switchman
467
474
  end
468
475
 
469
476
  def description
470
- [database_server.id, name].compact.join(':')
477
+ [database_server.id, name].compact.join(":")
471
478
  end
472
479
 
473
480
  # Shards are always on the default shard
@@ -501,7 +508,7 @@ module Switchman
501
508
  end
502
509
 
503
510
  def drop_database
504
- raise('Cannot drop the database of the default shard') if default?
511
+ raise("Cannot drop the database of the default shard") if default?
505
512
  return unless read_attribute(:name)
506
513
 
507
514
  begin
@@ -510,12 +517,12 @@ module Switchman
510
517
  drop_statement = sharding_config[adapter]&.[](:drop_statement)
511
518
  drop_statement ||= sharding_config[:drop_statement]
512
519
  if drop_statement
513
- drop_statement = Array(drop_statement).dup.
514
- map { |statement| statement.gsub('%{name}', name) }
520
+ drop_statement = Array(drop_statement).dup
521
+ .map { |statement| statement.gsub("%{name}", name) }
515
522
  end
516
523
 
517
524
  case adapter
518
- when 'mysql', 'mysql2'
525
+ when "mysql", "mysql2"
519
526
  activate do
520
527
  ::GuardRail.activate(:deploy) do
521
528
  drop_statement ||= "DROP DATABASE #{name}"
@@ -524,7 +531,7 @@ module Switchman
524
531
  end
525
532
  end
526
533
  end
527
- when 'postgresql'
534
+ when "postgresql"
528
535
  activate do
529
536
  ::GuardRail.activate(:deploy) do
530
537
  # Shut up, Postgres!
@@ -561,7 +568,7 @@ module Switchman
561
568
  end
562
569
 
563
570
  def destroy
564
- raise('Cannot destroy the default shard') if default?
571
+ raise("Cannot destroy the default shard") if default?
565
572
 
566
573
  super
567
574
  end
@@ -570,8 +577,8 @@ module Switchman
570
577
 
571
578
  def clear_cache
572
579
  Shard.default.activate do
573
- Switchman.cache.delete(['shard', id].join('/'))
574
- Switchman.cache.delete('default_shard') if default?
580
+ Switchman.cache.delete(["shard", id].join("/"))
581
+ Switchman.cache.delete("default_shard") if default?
575
582
  end
576
583
  self.class.clear_cache
577
584
  end
@@ -16,7 +16,11 @@ module Switchman
16
16
  payload[:shard] = {
17
17
  database_server_id: shard.database_server.id,
18
18
  id: shard.id,
19
- env: ::Rails.version < '7.0' ? @shard_host.pool.connection_klass&.current_role : @shard_host.pool.connection_class&.current_role
19
+ env: if ::Rails.version < "7.0"
20
+ @shard_host.pool.connection_klass&.current_role
21
+ else
22
+ @shard_host.pool.connection_class&.current_role
23
+ end
20
24
  }
21
25
  end
22
26
  super name, payload
@@ -65,7 +65,7 @@ module Switchman
65
65
  if server == Shard.default.database_server
66
66
  server.shards.where(name: name).first
67
67
  else
68
- shard = Shard.where('database_server_id IS NOT NULL AND name=?', name).first
68
+ shard = Shard.where("database_server_id IS NOT NULL AND name=?", name).first
69
69
  # if somehow databases got created in a different order, change the shard to match
70
70
  shard.database_server = server if shard
71
71
  shard
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Switchman
4
- VERSION = '3.4.2'
4
+ VERSION = "3.5.0"
5
5
  end
data/lib/switchman.rb CHANGED
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'guard_rail'
4
- require 'zeitwerk'
3
+ require "guard_rail"
4
+ require "zeitwerk"
5
5
 
6
6
  class SwitchmanInflector < Zeitwerk::GemInflector
7
7
  def camelize(basename, abspath)
8
8
  if basename =~ /\Apostgresql_(.*)/
9
- 'PostgreSQL' + super($1, abspath)
9
+ "PostgreSQL" + super($1, abspath)
10
10
  else
11
11
  super
12
12
  end
@@ -32,7 +32,13 @@ module Switchman
32
32
  end
33
33
 
34
34
  def self.foreign_key_check(name, type, limit: nil)
35
- puts "WARNING: All foreign keys need to be 8-byte integers. #{name} looks like a foreign key. If so, please add the option: `:limit => 8`" if name.to_s =~ /_id\z/ && type.to_s == 'integer' && limit.to_i < 8
35
+ return unless name.to_s.end_with?("_id") && type.to_s == "integer" && limit.to_i < 8
36
+
37
+ puts <<~TEXT.squish
38
+ WARNING: All foreign keys need to be 8-byte integers.
39
+ #{name} looks like a foreign key.
40
+ If so, please add the option: `:limit => 8`
41
+ TEXT
36
42
  end
37
43
 
38
44
  class OrderOnMultiShardQuery < RuntimeError; end