switchman 3.0.6 → 3.1.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 +4 -4
- data/Rakefile +1 -1
- data/lib/switchman/action_controller/caching.rb +2 -2
- data/lib/switchman/active_record/abstract_adapter.rb +2 -4
- data/lib/switchman/active_record/associations.rb +223 -0
- data/lib/switchman/active_record/attribute_methods.rb +144 -84
- data/lib/switchman/active_record/base.rb +71 -31
- data/lib/switchman/active_record/calculations.rb +12 -5
- data/lib/switchman/active_record/connection_pool.rb +2 -4
- data/lib/switchman/active_record/database_configurations.rb +18 -2
- data/lib/switchman/active_record/finder_methods.rb +2 -2
- data/lib/switchman/active_record/model_schema.rb +1 -1
- data/lib/switchman/active_record/persistence.rb +3 -5
- data/lib/switchman/active_record/postgresql_adapter.rb +1 -1
- data/lib/switchman/active_record/query_methods.rb +23 -14
- data/lib/switchman/active_record/reflection.rb +1 -1
- data/lib/switchman/active_record/relation.rb +15 -18
- data/lib/switchman/active_record/statement_cache.rb +2 -2
- data/lib/switchman/active_record/table_definition.rb +1 -1
- data/lib/switchman/active_support/cache.rb +16 -0
- data/lib/switchman/arel.rb +28 -6
- data/lib/switchman/database_server.rb +28 -20
- data/lib/switchman/default_shard.rb +0 -2
- data/lib/switchman/engine.rb +63 -125
- data/lib/switchman/errors.rb +4 -2
- data/lib/switchman/guard_rail/relation.rb +6 -9
- data/lib/switchman/guard_rail.rb +5 -0
- data/lib/switchman/parallel.rb +68 -0
- data/lib/switchman/r_spec_helper.rb +8 -0
- data/lib/switchman/rails.rb +1 -4
- data/{app/models → lib}/switchman/shard.rb +30 -131
- data/lib/switchman/sharded_instrumenter.rb +1 -1
- data/lib/switchman/standard_error.rb +10 -11
- data/{app/models → lib}/switchman/unsharded_record.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- data/lib/switchman.rb +22 -2
- data/lib/tasks/switchman.rake +16 -9
- metadata +19 -18
- data/lib/switchman/active_record/association.rb +0 -206
- data/lib/switchman/open4.rb +0 -80
|
@@ -8,15 +8,12 @@ module Switchman
|
|
|
8
8
|
|
|
9
9
|
class << self
|
|
10
10
|
attr_accessor :creating_new_shard
|
|
11
|
+
attr_reader :all_roles
|
|
11
12
|
|
|
12
13
|
def all
|
|
13
14
|
database_servers.values
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
def all_roles
|
|
17
|
-
@all_roles ||= all.map(&:roles).flatten.uniq
|
|
18
|
-
end
|
|
19
|
-
|
|
20
17
|
def find(id_or_all)
|
|
21
18
|
return all if id_or_all == :all
|
|
22
19
|
return id_or_all.map { |id| database_servers[id || ::Rails.env] }.compact.uniq if id_or_all.is_a?(Array)
|
|
@@ -38,7 +35,7 @@ module Switchman
|
|
|
38
35
|
database_servers[server.id] = server
|
|
39
36
|
::ActiveRecord::Base.configurations.configurations <<
|
|
40
37
|
::ActiveRecord::DatabaseConfigurations::HashConfig.new(::Rails.env, "#{server.id}/primary", settings)
|
|
41
|
-
Shard.send(:
|
|
38
|
+
Shard.send(:configure_connects_to)
|
|
42
39
|
server
|
|
43
40
|
end
|
|
44
41
|
|
|
@@ -49,31 +46,42 @@ module Switchman
|
|
|
49
46
|
servers[rand(servers.length)]
|
|
50
47
|
end
|
|
51
48
|
|
|
49
|
+
def guard_servers
|
|
50
|
+
all.each { |db| db.guard! if db.config[:prefer_secondary] }
|
|
51
|
+
end
|
|
52
|
+
|
|
52
53
|
private
|
|
53
54
|
|
|
54
55
|
def reference_role(role)
|
|
55
56
|
return if all_roles.include?(role)
|
|
56
57
|
|
|
57
58
|
@all_roles << role
|
|
58
|
-
Shard.send(:
|
|
59
|
+
Shard.send(:configure_connects_to)
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
def database_servers
|
|
62
63
|
if !@database_servers || @database_servers.empty?
|
|
63
64
|
@database_servers = {}.with_indifferent_access
|
|
65
|
+
roles = []
|
|
64
66
|
::ActiveRecord::Base.configurations.configurations.each do |config|
|
|
65
67
|
if config.name.include?('/')
|
|
66
68
|
name, role = config.name.split('/')
|
|
67
69
|
else
|
|
68
70
|
name, role = config.env_name, config.name
|
|
69
71
|
end
|
|
72
|
+
role = role.to_sym
|
|
70
73
|
|
|
71
|
-
|
|
74
|
+
roles << role
|
|
75
|
+
if role == :primary
|
|
72
76
|
@database_servers[name] = DatabaseServer.new(config.env_name, config.configuration_hash)
|
|
73
77
|
else
|
|
74
78
|
@database_servers[name].roles << role
|
|
75
79
|
end
|
|
76
80
|
end
|
|
81
|
+
# Do this after so that all database servers for all roles are established and we won't prematurely
|
|
82
|
+
# configure a connection for the wrong role
|
|
83
|
+
@all_roles = roles.uniq
|
|
84
|
+
Shard.send(:configure_connects_to)
|
|
77
85
|
end
|
|
78
86
|
@database_servers
|
|
79
87
|
end
|
|
@@ -89,13 +97,13 @@ module Switchman
|
|
|
89
97
|
end
|
|
90
98
|
|
|
91
99
|
def connects_to_hash
|
|
92
|
-
self.class.all_roles.
|
|
100
|
+
self.class.all_roles.to_h do |role|
|
|
93
101
|
config_role = role
|
|
94
102
|
config_role = :primary unless roles.include?(role)
|
|
95
103
|
config_name = :"#{id}/#{config_role}"
|
|
96
104
|
config_name = :primary if id == ::Rails.env && config_role == :primary
|
|
97
105
|
[role.to_sym, config_name]
|
|
98
|
-
end
|
|
106
|
+
end
|
|
99
107
|
end
|
|
100
108
|
|
|
101
109
|
def destroy
|
|
@@ -129,27 +137,27 @@ module Switchman
|
|
|
129
137
|
end
|
|
130
138
|
end
|
|
131
139
|
|
|
132
|
-
def guard_rail_environment
|
|
133
|
-
@guard_rail_environment || ::GuardRail.environment
|
|
134
|
-
end
|
|
135
|
-
|
|
136
140
|
# locks this db to a specific environment, except for
|
|
137
141
|
# when doing writes (then it falls back to the current
|
|
138
142
|
# value of GuardRail.environment)
|
|
139
143
|
def guard!(environment = :secondary)
|
|
140
|
-
|
|
144
|
+
DatabaseServer.send(:reference_role, environment)
|
|
145
|
+
::ActiveRecord::Base.connected_to_stack << { shard_roles: { id.to_sym => environment }, klasses: [::ActiveRecord::Base] }
|
|
141
146
|
end
|
|
142
147
|
|
|
143
148
|
def unguard!
|
|
144
|
-
|
|
149
|
+
::ActiveRecord::Base.connected_to_stack << { shard_roles: { id.to_sym => :_switchman_inherit }, klasses: [::ActiveRecord::Base] }
|
|
145
150
|
end
|
|
146
151
|
|
|
147
152
|
def unguard
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
+
return yield unless ::ActiveRecord::Base.role_overriden?(id.to_sym)
|
|
154
|
+
|
|
155
|
+
begin
|
|
156
|
+
unguard!
|
|
157
|
+
yield
|
|
158
|
+
ensure
|
|
159
|
+
::ActiveRecord::Base.connected_to_stack.pop
|
|
160
|
+
end
|
|
153
161
|
end
|
|
154
162
|
|
|
155
163
|
def shards
|
data/lib/switchman/engine.rb
CHANGED
|
@@ -8,102 +8,27 @@ module Switchman
|
|
|
8
8
|
config.active_record.legacy_connection_handling = false
|
|
9
9
|
config.active_record.writing_role = :primary
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
::GuardRail.singleton_class.prepend(GuardRail::ClassMethods)
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
result[key] = ::ActiveSupport::Cache.lookup_store(value)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
cache_store_config.each do |key, value| # rubocop:disable Style/CombinableLoops
|
|
22
|
-
next unless value.is_a?(String)
|
|
23
|
-
|
|
24
|
-
result[key] = result[value]
|
|
25
|
-
end
|
|
26
|
-
result
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
initializer 'switchman.initialize_cache', before: 'initialize_cache' do
|
|
30
|
-
require 'switchman/active_support/cache'
|
|
31
|
-
::ActiveSupport::Cache.singleton_class.prepend(ActiveSupport::Cache::ClassMethods)
|
|
32
|
-
|
|
33
|
-
# if we haven't already setup our cache map out-of-band, set it up from
|
|
34
|
-
# config.cache_store now. behaves similarly to Rails' default
|
|
35
|
-
# initialize_cache initializer, but for each value in the map, rather
|
|
36
|
-
# than just Rails.cache. if config.cache_store is a flat value, uses it
|
|
37
|
-
# to fill just the Rails.env entry in the cache map.
|
|
38
|
-
unless Switchman.config[:cache_map].present?
|
|
39
|
-
cache_store_config = ::Rails.configuration.cache_store
|
|
40
|
-
cache_store_config = { ::Rails.env => cache_store_config } unless cache_store_config.is_a?(Hash)
|
|
41
|
-
|
|
42
|
-
Switchman.config[:cache_map] = Engine.lookup_stores(cache_store_config)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# if the configured cache map (either from before, or as populated from
|
|
46
|
-
# config.cache_store) didn't have an entry for Rails.env, add one using
|
|
47
|
-
# lookup_store(nil); matches the behavior of Rails' default
|
|
48
|
-
# initialize_cache initializer when config.cache_store is nil.
|
|
49
|
-
unless Switchman.config[:cache_map].key?(::Rails.env)
|
|
50
|
-
value = ::ActiveSupport::Cache.lookup_store(nil)
|
|
51
|
-
Switchman.config[:cache_map][::Rails.env] = value
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
middlewares = Switchman.config[:cache_map].values.map do |store|
|
|
55
|
-
store.middleware if store.respond_to?(:middleware)
|
|
56
|
-
end.compact.uniq
|
|
57
|
-
middlewares.each do |middleware|
|
|
58
|
-
config.middleware.insert_before('Rack::Runtime', middleware)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# prevent :initialize_cache from trying to (or needing to) set
|
|
62
|
-
# Rails.cache. once our switchman.extend_ar initializer (below) runs
|
|
63
|
-
# Rails.cache will be overridden to pull appropriate values from the
|
|
64
|
-
# cache map, but between now and then, Rails.cache should return the
|
|
65
|
-
# Rails.env entry in the cache map.
|
|
66
|
-
::Rails.cache = Switchman.config[:cache_map][::Rails.env]
|
|
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 }
|
|
17
|
+
initialize_dependency_mechanism.instance_variable_get(:@options)[:after] = :set_autoload_paths
|
|
67
18
|
end
|
|
68
19
|
|
|
69
|
-
initializer 'switchman.
|
|
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
|
|
70
23
|
::ActiveSupport.on_load(:active_record) do
|
|
71
|
-
|
|
72
|
-
require '
|
|
73
|
-
require 'switchman/active_record/attribute_methods'
|
|
74
|
-
require 'switchman/active_record/base'
|
|
75
|
-
require 'switchman/active_record/calculations'
|
|
76
|
-
require 'switchman/active_record/connection_pool'
|
|
77
|
-
require 'switchman/active_record/database_configurations'
|
|
78
|
-
require 'switchman/active_record/database_configurations/database_config'
|
|
79
|
-
require 'switchman/active_record/finder_methods'
|
|
80
|
-
require 'switchman/active_record/log_subscriber'
|
|
81
|
-
require 'switchman/active_record/migration'
|
|
82
|
-
require 'switchman/active_record/model_schema'
|
|
83
|
-
require 'switchman/active_record/persistence'
|
|
84
|
-
require 'switchman/active_record/predicate_builder'
|
|
85
|
-
require 'switchman/active_record/query_cache'
|
|
86
|
-
require 'switchman/active_record/query_methods'
|
|
87
|
-
require 'switchman/active_record/reflection'
|
|
88
|
-
require 'switchman/active_record/relation'
|
|
89
|
-
require 'switchman/active_record/spawn_methods'
|
|
90
|
-
require 'switchman/active_record/statement_cache'
|
|
91
|
-
require 'switchman/active_record/tasks/database_tasks'
|
|
92
|
-
require 'switchman/active_record/type_caster'
|
|
93
|
-
require 'switchman/active_record/test_fixtures'
|
|
94
|
-
require 'switchman/arel'
|
|
95
|
-
require 'switchman/call_super'
|
|
96
|
-
require 'switchman/rails'
|
|
97
|
-
require 'switchman/guard_rail/relation'
|
|
98
|
-
require 'switchman/standard_error'
|
|
99
|
-
|
|
100
|
-
::StandardError.include(StandardError)
|
|
24
|
+
# Switchman requires postgres, so just always load the pg adapter
|
|
25
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
|
101
26
|
|
|
102
27
|
self.default_shard = ::Rails.env.to_sym
|
|
103
28
|
self.default_role = :primary
|
|
104
29
|
|
|
105
30
|
prepend ActiveRecord::Base
|
|
106
|
-
|
|
31
|
+
prepend ActiveRecord::AttributeMethods
|
|
107
32
|
include ActiveRecord::Persistence
|
|
108
33
|
singleton_class.prepend ActiveRecord::ModelSchema::ClassMethods
|
|
109
34
|
|
|
@@ -112,22 +37,24 @@ module Switchman
|
|
|
112
37
|
::ActiveRecord::StatementCache::BindMap.prepend(ActiveRecord::StatementCache::BindMap)
|
|
113
38
|
::ActiveRecord::StatementCache::Substitute.send(:attr_accessor, :primary, :sharded)
|
|
114
39
|
|
|
115
|
-
::ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecord::CollectionAssociation)
|
|
116
|
-
::ActiveRecord::Associations::HasOneAssociation.prepend(ActiveRecord::ForeignAssociation)
|
|
117
|
-
::ActiveRecord::Associations::HasManyAssociation.prepend(ActiveRecord::ForeignAssociation)
|
|
40
|
+
::ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecord::Associations::CollectionAssociation)
|
|
41
|
+
::ActiveRecord::Associations::HasOneAssociation.prepend(ActiveRecord::Associations::ForeignAssociation)
|
|
42
|
+
::ActiveRecord::Associations::HasManyAssociation.prepend(ActiveRecord::Associations::ForeignAssociation)
|
|
118
43
|
|
|
119
44
|
::ActiveRecord::PredicateBuilder.singleton_class.prepend(ActiveRecord::PredicateBuilder)
|
|
120
45
|
|
|
121
|
-
prepend(ActiveRecord::AutosaveAssociation)
|
|
46
|
+
prepend(ActiveRecord::Associations::AutosaveAssociation)
|
|
122
47
|
|
|
123
|
-
::ActiveRecord::Associations::Association.prepend(ActiveRecord::Association)
|
|
124
|
-
::ActiveRecord::Associations::BelongsToAssociation.prepend(ActiveRecord::BelongsToAssociation)
|
|
125
|
-
::ActiveRecord::Associations::CollectionProxy.include(ActiveRecord::CollectionProxy)
|
|
48
|
+
::ActiveRecord::Associations::Association.prepend(ActiveRecord::Associations::Association)
|
|
49
|
+
::ActiveRecord::Associations::BelongsToAssociation.prepend(ActiveRecord::Associations::BelongsToAssociation)
|
|
50
|
+
::ActiveRecord::Associations::CollectionProxy.include(ActiveRecord::Associations::CollectionProxy)
|
|
126
51
|
|
|
127
|
-
::ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecord::Preloader::Association)
|
|
52
|
+
::ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecord::Associations::Preloader::Association)
|
|
53
|
+
::ActiveRecord::Associations::Preloader::Association::LoaderQuery.prepend(ActiveRecord::Associations::Preloader::Association::LoaderQuery) unless ::Rails.version < '7.0'
|
|
128
54
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::AbstractAdapter)
|
|
129
55
|
::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ActiveRecord::ConnectionPool)
|
|
130
56
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::QueryCache)
|
|
57
|
+
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter)
|
|
131
58
|
|
|
132
59
|
::ActiveRecord::DatabaseConfigurations.prepend(ActiveRecord::DatabaseConfigurations)
|
|
133
60
|
::ActiveRecord::DatabaseConfigurations::DatabaseConfig.prepend(ActiveRecord::DatabaseConfigurations::DatabaseConfig)
|
|
@@ -160,54 +87,65 @@ module Switchman
|
|
|
160
87
|
::ActiveRecord::TypeCaster::Map.include(ActiveRecord::TypeCaster::Map)
|
|
161
88
|
::ActiveRecord::TypeCaster::Connection.include(ActiveRecord::TypeCaster::Connection)
|
|
162
89
|
|
|
163
|
-
::Rails.singleton_class.prepend(Rails::ClassMethods)
|
|
164
|
-
|
|
165
90
|
::Arel::Table.prepend(Arel::Table)
|
|
166
91
|
::Arel::Visitors::ToSql.prepend(Arel::Visitors::ToSql)
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def self.foreign_key_check(name, type, limit: nil)
|
|
171
|
-
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
|
|
172
|
-
end
|
|
173
92
|
|
|
174
|
-
initializer 'switchman.extend_connection_adapters', after: 'active_record.initialize_database' do
|
|
175
|
-
::ActiveSupport.on_load(:active_record) do
|
|
176
93
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.descendants.each do |klass|
|
|
177
94
|
klass.prepend(ActiveRecord::AbstractAdapter::ForeignKeyCheck)
|
|
178
95
|
end
|
|
179
96
|
|
|
180
|
-
require 'switchman/active_record/table_definition'
|
|
181
97
|
::ActiveRecord::ConnectionAdapters::TableDefinition.prepend(ActiveRecord::TableDefinition)
|
|
182
|
-
|
|
183
|
-
if defined?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
|
|
184
|
-
require 'switchman/active_record/postgresql_adapter'
|
|
185
|
-
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter)
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
Shard.send(:initialize_sharding)
|
|
189
98
|
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
|
|
100
|
+
::ActiveRecord::Base
|
|
190
101
|
end
|
|
191
102
|
|
|
192
|
-
initializer 'switchman.
|
|
193
|
-
::ActiveSupport.on_load(:
|
|
194
|
-
|
|
195
|
-
require 'active_record/base'
|
|
103
|
+
initializer 'switchman.error_patch', after: 'active_record.initialize_database' do
|
|
104
|
+
::ActiveSupport.on_load(:active_record) do
|
|
105
|
+
::StandardError.include(StandardError)
|
|
196
106
|
end
|
|
197
107
|
end
|
|
198
108
|
|
|
199
|
-
initializer 'switchman.
|
|
200
|
-
::ActiveSupport.
|
|
201
|
-
require 'switchman/guard_rail'
|
|
109
|
+
initializer 'switchman.initialize_cache', before: :initialize_cache, after: 'active_record.initialize_database' do
|
|
110
|
+
::ActiveSupport::Cache.singleton_class.prepend(ActiveSupport::Cache::ClassMethods)
|
|
202
111
|
|
|
203
|
-
|
|
112
|
+
# if we haven't already setup our cache map out-of-band, set it up from
|
|
113
|
+
# config.cache_store now. behaves similarly to Rails' default
|
|
114
|
+
# initialize_cache initializer, but for each value in the map, rather
|
|
115
|
+
# than just Rails.cache. if config.cache_store is a flat value, uses it
|
|
116
|
+
# to fill just the Rails.env entry in the cache map.
|
|
117
|
+
unless Switchman.config[:cache_map].present?
|
|
118
|
+
cache_store_config = ::Rails.configuration.cache_store
|
|
119
|
+
cache_store_config = { ::Rails.env => cache_store_config } unless cache_store_config.is_a?(Hash)
|
|
120
|
+
|
|
121
|
+
Switchman.config[:cache_map] = ::ActiveSupport::Cache.lookup_stores(cache_store_config)
|
|
204
122
|
end
|
|
205
|
-
end
|
|
206
123
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
124
|
+
# if the configured cache map (either from before, or as populated from
|
|
125
|
+
# config.cache_store) didn't have an entry for Rails.env, add one using
|
|
126
|
+
# lookup_store(nil); matches the behavior of Rails' default
|
|
127
|
+
# initialize_cache initializer when config.cache_store is nil.
|
|
128
|
+
unless Switchman.config[:cache_map].key?(::Rails.env)
|
|
129
|
+
value = ::ActiveSupport::Cache.lookup_store(nil)
|
|
130
|
+
Switchman.config[:cache_map][::Rails.env] = value
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
middlewares = Switchman.config[:cache_map].values.map do |store|
|
|
134
|
+
store.middleware if store.respond_to?(:middleware)
|
|
135
|
+
end.compact.uniq
|
|
136
|
+
middlewares.each do |middleware|
|
|
137
|
+
config.middleware.insert_before('Rack::Runtime', middleware)
|
|
138
|
+
end
|
|
210
139
|
|
|
140
|
+
# prevent :initialize_cache from trying to (or needing to) set
|
|
141
|
+
# Rails.cache. once our switchman.extend_ar initializer (below) runs
|
|
142
|
+
# Rails.cache will be overridden to pull appropriate values from the
|
|
143
|
+
# cache map, but between now and then, Rails.cache should return the
|
|
144
|
+
# Rails.env entry in the cache map.
|
|
145
|
+
::Rails.cache = Switchman.config[:cache_map][::Rails.env]
|
|
146
|
+
::Rails.singleton_class.prepend(Rails::ClassMethods)
|
|
147
|
+
|
|
148
|
+
::ActiveSupport.on_load(:action_controller) do
|
|
211
149
|
::ActionController::Base.include(ActionController::Caching)
|
|
212
150
|
end
|
|
213
151
|
end
|
data/lib/switchman/errors.rb
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Switchman
|
|
4
|
-
|
|
4
|
+
module Errors
|
|
5
|
+
class NonExistentShardError < RuntimeError; end
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
class ParallelShardExecError < RuntimeError; end
|
|
8
|
+
end
|
|
7
9
|
end
|
|
@@ -5,21 +5,18 @@ module Switchman
|
|
|
5
5
|
module Relation
|
|
6
6
|
def exec_queries(*args)
|
|
7
7
|
if lock_value
|
|
8
|
-
db = Shard.current(
|
|
9
|
-
|
|
8
|
+
db = Shard.current(connection_class_for_self).database_server
|
|
9
|
+
db.unguard { super }
|
|
10
|
+
else
|
|
11
|
+
super
|
|
10
12
|
end
|
|
11
|
-
super
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
%w[update_all delete_all].each do |method|
|
|
15
16
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
16
17
|
def #{method}(*args)
|
|
17
|
-
db = Shard.current(
|
|
18
|
-
|
|
19
|
-
db.unguard { super }
|
|
20
|
-
else
|
|
21
|
-
super
|
|
22
|
-
end
|
|
18
|
+
db = Shard.current(connection_class_for_self).database_server
|
|
19
|
+
db.unguard { super }
|
|
23
20
|
end
|
|
24
21
|
RUBY
|
|
25
22
|
end
|
data/lib/switchman/guard_rail.rb
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
module Switchman
|
|
4
4
|
module GuardRail
|
|
5
5
|
module ClassMethods
|
|
6
|
+
def environment
|
|
7
|
+
# no overrides so we get the global role, not the role for the default shard
|
|
8
|
+
::ActiveRecord::Base.current_role(without_overrides: true)
|
|
9
|
+
end
|
|
10
|
+
|
|
6
11
|
def activate(role)
|
|
7
12
|
DatabaseServer.send(:reference_role, role)
|
|
8
13
|
super
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'parallel'
|
|
4
|
+
|
|
5
|
+
module Switchman
|
|
6
|
+
module Parallel
|
|
7
|
+
module UndumpableException
|
|
8
|
+
def initialize(original)
|
|
9
|
+
super
|
|
10
|
+
@active_shards = original.instance_variable_get(:@active_shards)
|
|
11
|
+
current_shard
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class QuietExceptionWrapper
|
|
16
|
+
attr_accessor :name
|
|
17
|
+
|
|
18
|
+
def initialize(name, wrapper)
|
|
19
|
+
@name = name
|
|
20
|
+
@wrapper = wrapper
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def exception
|
|
24
|
+
@wrapper.exception
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class UndumpableResult
|
|
29
|
+
attr_reader :name
|
|
30
|
+
|
|
31
|
+
def initialize(result)
|
|
32
|
+
@name = result.inspect
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def inspect
|
|
36
|
+
"#<UndumpableResult:#{name}>"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class ResultWrapper
|
|
41
|
+
attr_reader :result
|
|
42
|
+
|
|
43
|
+
def initialize(result)
|
|
44
|
+
@result =
|
|
45
|
+
begin
|
|
46
|
+
Marshal.dump(result) && result
|
|
47
|
+
rescue
|
|
48
|
+
UndumpableResult.new(result)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class PrefixingIO
|
|
54
|
+
delegate_missing_to :@original_io
|
|
55
|
+
|
|
56
|
+
def initialize(prefix, original_io)
|
|
57
|
+
@prefix = prefix
|
|
58
|
+
@original_io = original_io
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def puts(*args)
|
|
62
|
+
args.flatten.each { |arg| @original_io.puts "#{@prefix}: #{arg}" }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
::Parallel::UndumpableException.prepend(::Switchman::Parallel::UndumpableException)
|
|
@@ -70,7 +70,10 @@ module Switchman
|
|
|
70
70
|
Shard.default(reload: true)
|
|
71
71
|
puts 'Done!'
|
|
72
72
|
|
|
73
|
+
main_pid = Process.pid
|
|
73
74
|
at_exit do
|
|
75
|
+
next unless main_pid == Process.pid
|
|
76
|
+
|
|
74
77
|
# preserve rspec's exit status
|
|
75
78
|
status = $!.is_a?(::SystemExit) ? $!.status : nil
|
|
76
79
|
puts 'Tearing down sharding for all specs'
|
|
@@ -107,6 +110,11 @@ module Switchman
|
|
|
107
110
|
raise 'Sharding did not set up correctly' if @@sharding_failed
|
|
108
111
|
|
|
109
112
|
Shard.clear_cache
|
|
113
|
+
if use_transactional_tests
|
|
114
|
+
Shard.default(reload: true)
|
|
115
|
+
@shard1 = Shard.find(@shard1.id)
|
|
116
|
+
@shard2 = Shard.find(@shard2.id)
|
|
117
|
+
end
|
|
110
118
|
end
|
|
111
119
|
|
|
112
120
|
klass.after do
|
data/lib/switchman/rails.rb
CHANGED
|
@@ -4,10 +4,7 @@ module Switchman
|
|
|
4
4
|
module Rails
|
|
5
5
|
module ClassMethods
|
|
6
6
|
def self.prepended(klass)
|
|
7
|
-
#
|
|
8
|
-
# Rails.cache(_without_sharding) to the value from the config file. but now
|
|
9
|
-
# that that's done (the bootstrap happened before this module is included
|
|
10
|
-
# into Rails), we want to make sure no one tries to assign to Rails.cache,
|
|
7
|
+
# we want to make sure no one tries to assign to Rails.cache,
|
|
11
8
|
# because it would be wrong w.r.t. sharding.
|
|
12
9
|
klass.send(:remove_method, :cache=)
|
|
13
10
|
end
|