switchman 3.4.2 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +15 -14
- data/db/migrate/20180828183945_add_default_shard_index.rb +1 -1
- data/db/migrate/20190114212900_add_unique_name_indexes.rb +10 -4
- data/lib/switchman/active_record/associations.rb +16 -5
- data/lib/switchman/active_record/attribute_methods.rb +67 -22
- data/lib/switchman/active_record/base.rb +32 -12
- data/lib/switchman/active_record/calculations.rb +37 -33
- data/lib/switchman/active_record/connection_pool.rb +4 -2
- data/lib/switchman/active_record/database_configurations.rb +12 -7
- data/lib/switchman/active_record/finder_methods.rb +1 -1
- data/lib/switchman/active_record/log_subscriber.rb +2 -2
- data/lib/switchman/active_record/migration.rb +4 -2
- data/lib/switchman/active_record/postgresql_adapter.rb +11 -10
- data/lib/switchman/active_record/query_cache.rb +1 -1
- data/lib/switchman/active_record/query_methods.rb +72 -26
- data/lib/switchman/active_record/relation.rb +13 -7
- data/lib/switchman/active_record/spawn_methods.rb +2 -2
- data/lib/switchman/active_record/statement_cache.rb +2 -2
- data/lib/switchman/active_record/tasks/database_tasks.rb +1 -1
- data/lib/switchman/active_record/test_fixtures.rb +19 -16
- data/lib/switchman/active_support/cache.rb +4 -1
- data/lib/switchman/arel.rb +6 -6
- data/lib/switchman/call_super.rb +1 -1
- data/lib/switchman/database_server.rb +20 -16
- data/lib/switchman/default_shard.rb +3 -3
- data/lib/switchman/engine.rb +33 -18
- data/lib/switchman/environment.rb +2 -2
- data/lib/switchman/errors.rb +4 -1
- data/lib/switchman/guard_rail/relation.rb +1 -1
- data/lib/switchman/parallel.rb +1 -1
- data/lib/switchman/r_spec_helper.rb +10 -10
- data/lib/switchman/shard.rb +30 -23
- data/lib/switchman/sharded_instrumenter.rb +5 -1
- data/lib/switchman/test_helper.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- data/lib/switchman.rb +10 -4
- data/lib/tasks/switchman.rake +40 -37
- metadata +9 -9
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
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.
|
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
|
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,
|
114
|
-
|
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 },
|
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 },
|
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(
|
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(
|
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(
|
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
|
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] ==
|
216
|
-
old_proc = ::ActiveRecord::Base.connection.raw_connection.set_notice_processor
|
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
|
-
|
6
|
+
"default"
|
7
7
|
end
|
8
|
-
|
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)
|
data/lib/switchman/engine.rb
CHANGED
@@ -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
|
14
|
-
#
|
15
|
-
|
16
|
-
|
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
|
21
|
-
before:
|
22
|
-
after: (::Rails.version <
|
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
|
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
|
-
|
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(
|
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(
|
81
|
-
|
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
|
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
|
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
|
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.
|
148
|
+
middlewares = Switchman.config[:cache_map].values.filter_map do |store|
|
134
149
|
store.middleware if store.respond_to?(:middleware)
|
135
|
-
end.
|
150
|
+
end.uniq
|
136
151
|
middlewares.each do |middleware|
|
137
|
-
config.middleware.insert_before(
|
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
|
3
|
+
require "etc"
|
4
4
|
|
5
5
|
module Switchman
|
6
6
|
class Environment
|
7
|
-
def self.cpu_count(nproc_bin =
|
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
|
data/lib/switchman/errors.rb
CHANGED
@@ -3,7 +3,10 @@
|
|
3
3
|
module Switchman
|
4
4
|
module Errors
|
5
5
|
class ManuallyCreatedShadowRecordError < RuntimeError
|
6
|
-
|
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 <=
|
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
|
data/lib/switchman/parallel.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
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
|
37
|
+
puts "Setting up sharding for all specs..."
|
38
38
|
Shard.delete_all
|
39
|
-
Switchman.cache.delete(
|
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
|
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(
|
69
|
+
Switchman.cache.delete("default_shard")
|
70
70
|
Shard.default(reload: true)
|
71
|
-
puts
|
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
|
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(
|
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
|
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(
|
135
|
+
Switchman.cache.delete("default_shard")
|
136
136
|
Shard.default(reload: true)
|
137
137
|
end
|
138
138
|
end
|
data/lib/switchman/shard.rb
CHANGED
@@ -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(
|
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
|
-
|
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,
|
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.
|
88
|
+
sharded_models.filter_map do |klass|
|
85
89
|
[klass, current(klass)]
|
86
|
-
end.
|
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 ==
|
92
|
-
return default if id_i == default.id || id.nil? || id ==
|
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([
|
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
|
-
|
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(
|
149
|
-
|
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: #{
|
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(
|
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
|
-
|
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
|
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
|
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(
|
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([
|
574
|
-
Switchman.cache.delete(
|
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 <
|
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(
|
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
|
data/lib/switchman/version.rb
CHANGED
data/lib/switchman.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
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
|
-
|
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
|
-
|
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
|