switchman 4.0.0 → 4.2.5
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 +4 -4
- data/lib/switchman/active_record/abstract_adapter.rb +10 -8
- data/lib/switchman/active_record/associations.rb +44 -60
- data/lib/switchman/active_record/attribute_methods.rb +34 -27
- data/lib/switchman/active_record/base.rb +12 -61
- data/lib/switchman/active_record/calculations.rb +38 -49
- data/lib/switchman/active_record/connection_pool.rb +37 -23
- data/lib/switchman/active_record/database_configurations.rb +7 -19
- data/lib/switchman/active_record/finder_methods.rb +21 -45
- data/lib/switchman/active_record/log_subscriber.rb +1 -10
- data/lib/switchman/active_record/migration.rb +14 -33
- data/lib/switchman/active_record/persistence.rb +1 -1
- data/lib/switchman/active_record/postgresql_adapter.rb +26 -12
- data/lib/switchman/active_record/query_cache.rb +22 -42
- data/lib/switchman/active_record/query_methods.rb +63 -22
- data/lib/switchman/active_record/reflection.rb +9 -2
- data/lib/switchman/active_record/relation.rb +64 -5
- data/lib/switchman/active_record/spawn_methods.rb +1 -5
- data/lib/switchman/active_record/statement_cache.rb +2 -2
- data/lib/switchman/active_record/table_definition.rb +1 -1
- data/lib/switchman/active_record/test_fixtures.rb +71 -35
- data/lib/switchman/arel.rb +0 -25
- data/lib/switchman/database_server.rb +5 -9
- data/lib/switchman/engine.rb +10 -5
- data/lib/switchman/shard.rb +10 -12
- data/lib/switchman/sharded_instrumenter.rb +8 -2
- data/lib/switchman/test_helper.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- data/lib/tasks/switchman.rake +1 -1
- metadata +25 -159
|
@@ -28,15 +28,15 @@ module Switchman
|
|
|
28
28
|
relation
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
def new(*, &
|
|
31
|
+
def new(*, &)
|
|
32
32
|
primary_shard.activate(klass.connection_class_for_self) { super }
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
def create(*, &
|
|
35
|
+
def create(*, &)
|
|
36
36
|
primary_shard.activate(klass.connection_class_for_self) { super }
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
def create!(*, &
|
|
39
|
+
def create!(*, &)
|
|
40
40
|
primary_shard.activate(klass.connection_class_for_self) { super }
|
|
41
41
|
end
|
|
42
42
|
|
|
@@ -73,6 +73,59 @@ module Switchman
|
|
|
73
73
|
RUBY
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
# https://github.com/rails/rails/commit/ed2c15b52450ff927a05629f031376f25b670335
|
|
77
|
+
# once the minimum version is Rails 7.2, we can drop this separate module
|
|
78
|
+
module InsertUpsertAll
|
|
79
|
+
%w[insert_all upsert_all].each do |method|
|
|
80
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
81
|
+
def #{method}(attributes, returning: nil, **)
|
|
82
|
+
scope = self != ::ActiveRecord::Base && current_scope
|
|
83
|
+
if (target_shard = scope&.primary_shard) == (current_shard = Shard.current(connection_class_for_self))
|
|
84
|
+
scope = nil
|
|
85
|
+
end
|
|
86
|
+
if scope
|
|
87
|
+
dupped = false
|
|
88
|
+
attributes.each_with_index do |hash, i|
|
|
89
|
+
if dupped || hash.any? { |k, v| sharded_column?(k) }
|
|
90
|
+
unless dupped
|
|
91
|
+
attributes = attributes.dup
|
|
92
|
+
dupped = true
|
|
93
|
+
end
|
|
94
|
+
attributes[i] = hash.to_h do |k, v|
|
|
95
|
+
if sharded_column?(k)
|
|
96
|
+
[k, Shard.relative_id_for(v, current_shard, target_shard)]
|
|
97
|
+
else
|
|
98
|
+
[k, v]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
if scope
|
|
106
|
+
scope.activate do
|
|
107
|
+
db = Shard.current(connection_class_for_self).database_server
|
|
108
|
+
result = db.unguard { super }
|
|
109
|
+
if result&.columns&.any? { |c| sharded_column?(c) }
|
|
110
|
+
transposed_rows = result.rows.map do |row|
|
|
111
|
+
row.map.with_index do |value, i|
|
|
112
|
+
sharded_column?(result.columns[i]) ? Shard.relative_id_for(value, target_shard, current_shard) : value
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
result = ::ActiveRecord::Result.new(result.columns, transposed_rows, result.column_types)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
result
|
|
119
|
+
end
|
|
120
|
+
else
|
|
121
|
+
db = Shard.current(connection_class_for_self).database_server
|
|
122
|
+
db.unguard { super }
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
RUBY
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
76
129
|
def find_ids_in_ranges(options = {})
|
|
77
130
|
is_integer = columns_hash[primary_key.to_s].type == :integer
|
|
78
131
|
loose_mode = options[:loose] && is_integer
|
|
@@ -110,7 +163,7 @@ module Switchman
|
|
|
110
163
|
end
|
|
111
164
|
end
|
|
112
165
|
|
|
113
|
-
def activate(unordered: false, &block)
|
|
166
|
+
def activate(count: false, unordered: false, &block)
|
|
114
167
|
shards = all_shards
|
|
115
168
|
if Array === shards && shards.length == 1
|
|
116
169
|
if !loaded? && shard_value != shards.first
|
|
@@ -136,7 +189,13 @@ module Switchman
|
|
|
136
189
|
|
|
137
190
|
shard_results = relation.activate(&block)
|
|
138
191
|
|
|
139
|
-
if shard_results.present? &&
|
|
192
|
+
if shard_results.present? && count
|
|
193
|
+
unless shard_results.is_a?(Integer)
|
|
194
|
+
raise "expected integer result for count, got #{shard_results.class.name}"
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
result_count += shard_results
|
|
198
|
+
elsif shard_results.present? && !unordered
|
|
140
199
|
can_order ||= can_order_cross_shard_results? unless order_values.empty?
|
|
141
200
|
raise OrderOnMultiShardQuery if !can_order && !order_values.empty? && result_count.positive?
|
|
142
201
|
|
|
@@ -62,16 +62,12 @@ module Switchman
|
|
|
62
62
|
if primary_shard != final_primary_shard && rhs.primary_shard != final_primary_shard
|
|
63
63
|
shard!(final_primary_shard)
|
|
64
64
|
rhs = rhs.shard(final_primary_shard)
|
|
65
|
-
super(rhs)
|
|
66
65
|
elsif primary_shard != final_primary_shard
|
|
67
66
|
shard!(final_primary_shard)
|
|
68
|
-
super(rhs)
|
|
69
67
|
elsif rhs.primary_shard != final_primary_shard
|
|
70
68
|
rhs = rhs.shard(final_primary_shard)
|
|
71
|
-
super(rhs)
|
|
72
|
-
else
|
|
73
|
-
super
|
|
74
69
|
end
|
|
70
|
+
super
|
|
75
71
|
|
|
76
72
|
self.shard_value = final_shard_value
|
|
77
73
|
self.shard_source_value = final_shard_source_value
|
|
@@ -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)
|
|
@@ -4,49 +4,85 @@ module Switchman
|
|
|
4
4
|
module ActiveRecord
|
|
5
5
|
module TestFixtures
|
|
6
6
|
FORBIDDEN_DB_ENVS = %i[development production].freeze
|
|
7
|
-
def setup_fixtures(config = ::ActiveRecord::Base)
|
|
8
|
-
super
|
|
9
|
-
|
|
10
|
-
return unless run_in_transaction?
|
|
11
|
-
|
|
12
|
-
# Replace the one that activerecord natively uses with a switchman-optimized one
|
|
13
|
-
::ActiveSupport::Notifications.unsubscribe(@connection_subscriber)
|
|
14
|
-
# Code adapted from the code in rails proper
|
|
15
|
-
@connection_subscriber =
|
|
16
|
-
::ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
|
|
17
|
-
spec_name = if ::Rails.version < "7.1"
|
|
18
|
-
payload[:spec_name] if payload.key?(:spec_name)
|
|
19
|
-
elsif payload.key?(:connection_name)
|
|
20
|
-
payload[:connection_name]
|
|
21
|
-
end
|
|
22
|
-
shard = payload[:shard] if payload.key?(:shard)
|
|
23
7
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
rescue ::ActiveRecord::ConnectionNotEstablished, ::ActiveRecord::NoDatabaseError
|
|
29
|
-
connection = nil
|
|
30
|
-
end
|
|
8
|
+
if ::Rails.version < "7.2"
|
|
9
|
+
def setup_fixtures(config = ::ActiveRecord::Base)
|
|
10
|
+
super
|
|
11
|
+
return unless run_in_transaction?
|
|
31
12
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
13
|
+
# Replace the one that activerecord natively uses with a switchman-optimized one
|
|
14
|
+
::ActiveSupport::Notifications.unsubscribe(@connection_subscriber)
|
|
15
|
+
# Code adapted from the code in rails proper
|
|
16
|
+
@connection_subscriber =
|
|
17
|
+
::ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
|
|
18
|
+
spec_name = (payload[:connection_name] if payload.key?(:connection_name))
|
|
19
|
+
shard = payload[:shard] if payload.key?(:shard)
|
|
20
|
+
|
|
21
|
+
if spec_name && !FORBIDDEN_DB_ENVS.include?(shard)
|
|
22
|
+
begin
|
|
23
|
+
connection = ::ActiveRecord::Base.connection_handler.retrieve_connection(spec_name, shard:)
|
|
24
|
+
connection.connect! # eagerly validate the connection
|
|
25
|
+
rescue ::ActiveRecord::ConnectionNotEstablished
|
|
26
|
+
connection = nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
if connection
|
|
30
|
+
setup_shared_connection_pool
|
|
31
|
+
unless @fixture_connections.include?(connection)
|
|
32
|
+
connection.begin_transaction joinable: false, _lazy: false
|
|
33
|
+
connection.pool.lock_thread = true if lock_threads
|
|
34
|
+
@fixture_connections << connection
|
|
35
|
+
end
|
|
38
36
|
end
|
|
39
37
|
end
|
|
40
38
|
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def enlist_fixture_connections
|
|
42
|
+
setup_shared_connection_pool
|
|
43
|
+
|
|
44
|
+
::ActiveRecord::Base.connection_handler.connection_pool_list(:primary).reject do |cp|
|
|
45
|
+
FORBIDDEN_DB_ENVS.include?(cp.db_config.env_name.to_sym)
|
|
46
|
+
end.map(&:connection)
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
def setup_transactional_fixtures
|
|
50
|
+
setup_shared_connection_pool
|
|
51
|
+
|
|
52
|
+
# Begin transactions for connections already established
|
|
53
|
+
# INST: :writing -> :primary
|
|
54
|
+
@fixture_connection_pools = ::ActiveRecord::Base.connection_handler.connection_pool_list(:primary)
|
|
55
|
+
# INST: filter by FORBIDDEN_DB_ENVS
|
|
56
|
+
@fixture_connection_pools = @fixture_connection_pools.reject do |cp|
|
|
57
|
+
FORBIDDEN_DB_ENVS.include?(cp.db_config.env_name.to_sym)
|
|
41
58
|
end
|
|
42
|
-
end
|
|
43
59
|
|
|
44
|
-
|
|
45
|
-
|
|
60
|
+
@fixture_connection_pools.each do |pool|
|
|
61
|
+
pool.pin_connection!(lock_threads)
|
|
62
|
+
pool.lease_connection
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# When connections are established in the future, begin a transaction too
|
|
66
|
+
@connection_subscriber = ::ActiveSupport::Notifications
|
|
67
|
+
.subscribe("!connection.active_record") do |_, _, _, _, payload|
|
|
68
|
+
connection_name = payload[:connection_name] if payload.key?(:connection_name)
|
|
69
|
+
shard = payload[:shard] if payload.key?(:shard)
|
|
70
|
+
|
|
71
|
+
# INST: filter by FORBIDDEN_DB_ENVS
|
|
72
|
+
if connection_name && !FORBIDDEN_DB_ENVS.include?(shard)
|
|
73
|
+
pool = ::ActiveRecord::Base.connection_handler.retrieve_connection_pool(connection_name, shard:)
|
|
74
|
+
if pool
|
|
75
|
+
setup_shared_connection_pool
|
|
46
76
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
77
|
+
unless @fixture_connection_pools.include?(pool)
|
|
78
|
+
pool.pin_connection!(lock_threads)
|
|
79
|
+
pool.lease_connection
|
|
80
|
+
@fixture_connection_pools << pool
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
50
86
|
end
|
|
51
87
|
end
|
|
52
88
|
end
|
data/lib/switchman/arel.rb
CHANGED
|
@@ -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
|
|
|
@@ -121,7 +121,7 @@ module Switchman
|
|
|
121
121
|
Shard.sharded_models.each do |klass|
|
|
122
122
|
self.class.all_roles.each do |role|
|
|
123
123
|
klass.connection_handler.remove_connection_pool(klass.connection_specification_name,
|
|
124
|
-
role
|
|
124
|
+
role:,
|
|
125
125
|
shard: id.to_sym)
|
|
126
126
|
end
|
|
127
127
|
end
|
|
@@ -218,7 +218,7 @@ module Switchman
|
|
|
218
218
|
if config_create_statement
|
|
219
219
|
create_commands = Array(config_create_statement).dup
|
|
220
220
|
create_statement = lambda {
|
|
221
|
-
create_commands.map { |statement| format(statement, name
|
|
221
|
+
create_commands.map { |statement| format(statement, name:, password:) }
|
|
222
222
|
}
|
|
223
223
|
end
|
|
224
224
|
|
|
@@ -236,8 +236,8 @@ module Switchman
|
|
|
236
236
|
self.class.creating_new_shard = true
|
|
237
237
|
DatabaseServer.send(:reference_role, :deploy)
|
|
238
238
|
::ActiveRecord::Base.connected_to(shard: self.id.to_sym, role: :deploy) do
|
|
239
|
-
shard = Shard.create!(id
|
|
240
|
-
name
|
|
239
|
+
shard = Shard.create!(id:,
|
|
240
|
+
name:,
|
|
241
241
|
database_server_id: self.id)
|
|
242
242
|
if create_statement
|
|
243
243
|
if ::ActiveRecord::Base.connection.select_value(
|
|
@@ -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
|
-
|
|
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|
|
data/lib/switchman/engine.rb
CHANGED
|
@@ -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,12 +45,18 @@ module Switchman
|
|
|
47
45
|
ActiveRecord::Associations::Preloader::Association::LoaderRecords
|
|
48
46
|
)
|
|
49
47
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::AbstractAdapter)
|
|
50
|
-
|
|
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)
|
|
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
|
|
56
60
|
|
|
57
61
|
::ActiveRecord::DatabaseConfigurations.prepend(ActiveRecord::DatabaseConfigurations)
|
|
58
62
|
::ActiveRecord::DatabaseConfigurations::DatabaseConfig.prepend(
|
|
@@ -79,6 +83,7 @@ module Switchman
|
|
|
79
83
|
::ActiveRecord::Relation.include(ActiveRecord::QueryMethods)
|
|
80
84
|
::ActiveRecord::Relation.prepend(GuardRail::Relation)
|
|
81
85
|
::ActiveRecord::Relation.prepend(ActiveRecord::Relation)
|
|
86
|
+
::ActiveRecord::Relation.prepend(ActiveRecord::Relation::InsertUpsertAll) if ::Rails.version >= "7.2"
|
|
82
87
|
::ActiveRecord::Relation.include(ActiveRecord::SpawnMethods)
|
|
83
88
|
::ActiveRecord::Relation.include(CallSuper)
|
|
84
89
|
|
data/lib/switchman/shard.rb
CHANGED
|
@@ -5,8 +5,10 @@ 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"
|
|
8
9
|
# only allow one default
|
|
9
10
|
validates_uniqueness_of :default, if: ->(s) { s.default? }
|
|
11
|
+
# rubocop:enable Style/SymbolProc
|
|
10
12
|
|
|
11
13
|
after_save :clear_cache
|
|
12
14
|
after_destroy :clear_cache
|
|
@@ -51,7 +53,7 @@ module Switchman
|
|
|
51
53
|
return [default] unless default.is_a?(Switchman::Shard)
|
|
52
54
|
return all if !Switchman.region || DatabaseServer.none?(&:region)
|
|
53
55
|
|
|
54
|
-
in_region(Switchman.region, include_regionless:
|
|
56
|
+
in_region(Switchman.region, include_regionless:)
|
|
55
57
|
end)
|
|
56
58
|
|
|
57
59
|
class << self
|
|
@@ -141,7 +143,7 @@ module Switchman
|
|
|
141
143
|
|
|
142
144
|
unless cached_shards.key?(id)
|
|
143
145
|
cached_shards[id] = Shard.default.activate do
|
|
144
|
-
find_cached(["shard", id]) { find_by(id:
|
|
146
|
+
find_cached(["shard", id]) { find_by(id:) }
|
|
145
147
|
end
|
|
146
148
|
end
|
|
147
149
|
cached_shards[id]
|
|
@@ -202,7 +204,7 @@ module Switchman
|
|
|
202
204
|
database_servers = scope.reorder("database_server_id").select(:database_server_id).distinct
|
|
203
205
|
.filter_map(&:database_server).uniq
|
|
204
206
|
# nothing to do
|
|
205
|
-
return if database_servers.
|
|
207
|
+
return if database_servers.none?
|
|
206
208
|
|
|
207
209
|
scopes = database_servers.to_h do |server|
|
|
208
210
|
[server, scope.merge(server.shards)]
|
|
@@ -214,11 +216,7 @@ module Switchman
|
|
|
214
216
|
# clear connections prior to forking (no more queries will be executed in the parent,
|
|
215
217
|
# and we want them gone so that we don't accidentally use them post-fork doing something
|
|
216
218
|
# silly like dealloc'ing prepared statements)
|
|
217
|
-
|
|
218
|
-
::ActiveRecord::Base.clear_all_connections!(nil)
|
|
219
|
-
else
|
|
220
|
-
::ActiveRecord::Base.connection_handler.clear_all_connections!(:all)
|
|
221
|
-
end
|
|
219
|
+
::ActiveRecord::Base.connection_handler.clear_all_connections!(:all)
|
|
222
220
|
|
|
223
221
|
parent_process_name = sanitized_process_title
|
|
224
222
|
ret = ::Parallel.map(scopes, in_processes: (scopes.length > 1) ? parallel : 0) do |server, subscope|
|
|
@@ -233,7 +231,7 @@ module Switchman
|
|
|
233
231
|
Switchman.config[:on_fork_proc]&.call
|
|
234
232
|
with_each_shard(subscope,
|
|
235
233
|
classes,
|
|
236
|
-
exception
|
|
234
|
+
exception:,
|
|
237
235
|
output: output || :decorated) do
|
|
238
236
|
last_description = Shard.current.description
|
|
239
237
|
Parallel::ResultWrapper.new(yield)
|
|
@@ -459,7 +457,7 @@ module Switchman
|
|
|
459
457
|
connects_to_hash.each do |(db_name, role_hash)|
|
|
460
458
|
role_hash.each_key do |role|
|
|
461
459
|
role_hash.delete(role) if klass.connection_handler.retrieve_connection_pool(
|
|
462
|
-
klass.connection_specification_name, role
|
|
460
|
+
klass.connection_specification_name, role:, shard: db_name
|
|
463
461
|
)
|
|
464
462
|
end
|
|
465
463
|
end
|
|
@@ -590,9 +588,9 @@ module Switchman
|
|
|
590
588
|
id
|
|
591
589
|
end
|
|
592
590
|
|
|
593
|
-
def activate(*classes, &
|
|
591
|
+
def activate(*classes, &)
|
|
594
592
|
shards = hashify_classes(classes)
|
|
595
|
-
Shard.activate(shards, &
|
|
593
|
+
Shard.activate(shards, &)
|
|
596
594
|
end
|
|
597
595
|
|
|
598
596
|
# for use from console ONLY
|
|
@@ -13,13 +13,19 @@ module Switchman
|
|
|
13
13
|
# when we might be doing a query while defining attribute methods,
|
|
14
14
|
# so just avoid logging then
|
|
15
15
|
if shard.is_a?(Shard) && Shard.instance_variable_get(:@attribute_methods_generated)
|
|
16
|
+
env = if ::Rails.version < "8.0"
|
|
17
|
+
@shard_host.pool.connection_class&.current_role
|
|
18
|
+
else
|
|
19
|
+
@shard_host.pool.connection_descriptor&.name&.constantize&.current_role
|
|
20
|
+
end
|
|
21
|
+
|
|
16
22
|
payload[:shard] = {
|
|
17
23
|
database_server_id: shard.database_server.id,
|
|
18
24
|
id: shard.id,
|
|
19
|
-
env:
|
|
25
|
+
env:
|
|
20
26
|
}
|
|
21
27
|
end
|
|
22
|
-
super
|
|
28
|
+
super
|
|
23
29
|
end
|
|
24
30
|
end
|
|
25
31
|
end
|
|
@@ -63,7 +63,7 @@ module Switchman
|
|
|
63
63
|
|
|
64
64
|
def find_existing_test_shard(server, name)
|
|
65
65
|
if server == Shard.default.database_server
|
|
66
|
-
server.shards.where(name:
|
|
66
|
+
server.shards.where(name:).first
|
|
67
67
|
else
|
|
68
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
|
data/lib/switchman/version.rb
CHANGED
data/lib/tasks/switchman.rake
CHANGED