switchman 3.0.14 → 3.5.20
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 +16 -15
- 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/action_controller/caching.rb +2 -2
- data/lib/switchman/active_record/abstract_adapter.rb +6 -6
- data/lib/switchman/active_record/associations.rb +365 -0
- data/lib/switchman/active_record/attribute_methods.rb +188 -99
- data/lib/switchman/active_record/base.rb +185 -40
- data/lib/switchman/active_record/calculations.rb +64 -40
- data/lib/switchman/active_record/connection_handler.rb +18 -0
- data/lib/switchman/active_record/connection_pool.rb +24 -5
- data/lib/switchman/active_record/database_configurations.rb +37 -13
- data/lib/switchman/active_record/finder_methods.rb +46 -16
- data/lib/switchman/active_record/log_subscriber.rb +11 -5
- data/lib/switchman/active_record/migration.rb +52 -8
- data/lib/switchman/active_record/model_schema.rb +1 -1
- data/lib/switchman/active_record/persistence.rb +31 -3
- data/lib/switchman/active_record/postgresql_adapter.rb +11 -10
- data/lib/switchman/active_record/predicate_builder.rb +2 -2
- data/lib/switchman/active_record/query_cache.rb +49 -20
- data/lib/switchman/active_record/query_methods.rb +187 -136
- data/lib/switchman/active_record/reflection.rb +1 -1
- data/lib/switchman/active_record/relation.rb +33 -26
- data/lib/switchman/active_record/spawn_methods.rb +2 -2
- data/lib/switchman/active_record/statement_cache.rb +11 -7
- data/lib/switchman/active_record/table_definition.rb +1 -1
- data/lib/switchman/active_record/tasks/database_tasks.rb +6 -1
- data/lib/switchman/active_record/test_fixtures.rb +26 -16
- data/lib/switchman/active_support/cache.rb +20 -1
- data/lib/switchman/arel.rb +34 -18
- data/lib/switchman/call_super.rb +8 -2
- data/lib/switchman/database_server.rb +91 -45
- data/lib/switchman/default_shard.rb +14 -5
- data/lib/switchman/engine.rb +79 -126
- data/lib/switchman/environment.rb +2 -2
- data/lib/switchman/errors.rb +17 -2
- data/lib/switchman/guard_rail/relation.rb +8 -10
- data/lib/switchman/guard_rail.rb +5 -0
- data/lib/switchman/parallel.rb +68 -0
- data/lib/switchman/r_spec_helper.rb +14 -11
- data/lib/switchman/rails.rb +2 -5
- data/{app/models → lib}/switchman/shard.rb +186 -189
- data/lib/switchman/sharded_instrumenter.rb +5 -1
- data/lib/switchman/shared_schema_cache.rb +11 -0
- data/lib/switchman/standard_error.rb +6 -5
- data/lib/switchman/test_helper.rb +2 -2
- data/{app/models → lib}/switchman/unsharded_record.rb +1 -1
- data/lib/switchman/version.rb +1 -1
- data/lib/switchman.rb +44 -12
- data/lib/tasks/switchman.rake +74 -53
- metadata +42 -53
- data/lib/switchman/active_record/association.rb +0 -206
- data/lib/switchman/open4.rb +0 -80
data/lib/switchman/engine.rb
CHANGED
|
@@ -5,109 +5,34 @@ module Switchman
|
|
|
5
5
|
isolate_namespace Switchman
|
|
6
6
|
|
|
7
7
|
# enable Rails 6.1 style connection handling
|
|
8
|
-
config.active_record.legacy_connection_handling = false
|
|
8
|
+
config.active_record.legacy_connection_handling = false if ::Rails.version < "7.1"
|
|
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
|
-
|
|
19
|
-
|
|
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
|
|
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
|
|
52
20
|
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]
|
|
21
|
+
initialize_dependency_mechanism.instance_variable_get(:@options)[:after] = :set_autoload_paths
|
|
67
22
|
end
|
|
68
23
|
|
|
69
|
-
initializer
|
|
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
|
|
70
27
|
::ActiveSupport.on_load(:active_record) do
|
|
71
28
|
# Switchman requires postgres, so just always load the pg adapter
|
|
72
|
-
require
|
|
73
|
-
|
|
74
|
-
require 'switchman/active_record/abstract_adapter'
|
|
75
|
-
require 'switchman/active_record/association'
|
|
76
|
-
require 'switchman/active_record/attribute_methods'
|
|
77
|
-
require 'switchman/active_record/base'
|
|
78
|
-
require 'switchman/active_record/calculations'
|
|
79
|
-
require 'switchman/active_record/connection_pool'
|
|
80
|
-
require 'switchman/active_record/database_configurations'
|
|
81
|
-
require 'switchman/active_record/database_configurations/database_config'
|
|
82
|
-
require 'switchman/active_record/finder_methods'
|
|
83
|
-
require 'switchman/active_record/log_subscriber'
|
|
84
|
-
require 'switchman/active_record/migration'
|
|
85
|
-
require 'switchman/active_record/model_schema'
|
|
86
|
-
require 'switchman/active_record/persistence'
|
|
87
|
-
require 'switchman/active_record/postgresql_adapter'
|
|
88
|
-
require 'switchman/active_record/predicate_builder'
|
|
89
|
-
require 'switchman/active_record/query_cache'
|
|
90
|
-
require 'switchman/active_record/query_methods'
|
|
91
|
-
require 'switchman/active_record/reflection'
|
|
92
|
-
require 'switchman/active_record/relation'
|
|
93
|
-
require 'switchman/active_record/spawn_methods'
|
|
94
|
-
require 'switchman/active_record/statement_cache'
|
|
95
|
-
require 'switchman/active_record/tasks/database_tasks'
|
|
96
|
-
require 'switchman/active_record/type_caster'
|
|
97
|
-
require 'switchman/active_record/test_fixtures'
|
|
98
|
-
require 'switchman/arel'
|
|
99
|
-
require 'switchman/call_super'
|
|
100
|
-
require 'switchman/rails'
|
|
101
|
-
require 'switchman/guard_rail/relation'
|
|
102
|
-
require 'switchman/standard_error'
|
|
103
|
-
|
|
104
|
-
::StandardError.include(StandardError)
|
|
29
|
+
require "active_record/connection_adapters/postgresql_adapter"
|
|
105
30
|
|
|
106
31
|
self.default_shard = ::Rails.env.to_sym
|
|
107
32
|
self.default_role = :primary
|
|
108
33
|
|
|
109
34
|
prepend ActiveRecord::Base
|
|
110
|
-
|
|
35
|
+
prepend ActiveRecord::AttributeMethods
|
|
111
36
|
include ActiveRecord::Persistence
|
|
112
37
|
singleton_class.prepend ActiveRecord::ModelSchema::ClassMethods
|
|
113
38
|
|
|
@@ -116,26 +41,36 @@ module Switchman
|
|
|
116
41
|
::ActiveRecord::StatementCache::BindMap.prepend(ActiveRecord::StatementCache::BindMap)
|
|
117
42
|
::ActiveRecord::StatementCache::Substitute.send(:attr_accessor, :primary, :sharded)
|
|
118
43
|
|
|
119
|
-
::ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecord::CollectionAssociation)
|
|
120
|
-
::ActiveRecord::Associations::HasOneAssociation.prepend(ActiveRecord::ForeignAssociation)
|
|
121
|
-
::ActiveRecord::Associations::HasManyAssociation.prepend(ActiveRecord::ForeignAssociation)
|
|
44
|
+
::ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecord::Associations::CollectionAssociation)
|
|
45
|
+
::ActiveRecord::Associations::HasOneAssociation.prepend(ActiveRecord::Associations::ForeignAssociation)
|
|
46
|
+
::ActiveRecord::Associations::HasManyAssociation.prepend(ActiveRecord::Associations::ForeignAssociation)
|
|
122
47
|
|
|
123
48
|
::ActiveRecord::PredicateBuilder.singleton_class.prepend(ActiveRecord::PredicateBuilder)
|
|
124
49
|
|
|
125
|
-
prepend(ActiveRecord::AutosaveAssociation)
|
|
50
|
+
prepend(ActiveRecord::Associations::AutosaveAssociation)
|
|
126
51
|
|
|
127
|
-
::ActiveRecord::Associations::Association.prepend(ActiveRecord::Association)
|
|
128
|
-
::ActiveRecord::Associations::BelongsToAssociation.prepend(ActiveRecord::BelongsToAssociation)
|
|
129
|
-
::ActiveRecord::Associations::CollectionProxy.include(ActiveRecord::CollectionProxy)
|
|
52
|
+
::ActiveRecord::Associations::Association.prepend(ActiveRecord::Associations::Association)
|
|
53
|
+
::ActiveRecord::Associations::BelongsToAssociation.prepend(ActiveRecord::Associations::BelongsToAssociation)
|
|
54
|
+
::ActiveRecord::Associations::CollectionProxy.include(ActiveRecord::Associations::CollectionProxy)
|
|
130
55
|
|
|
131
|
-
::ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecord::Preloader::Association)
|
|
56
|
+
::ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecord::Associations::Preloader::Association)
|
|
57
|
+
unless ::Rails.version < "7.0"
|
|
58
|
+
::ActiveRecord::Associations::Preloader::Association::LoaderRecords.prepend(
|
|
59
|
+
ActiveRecord::Associations::Preloader::Association::LoaderRecords
|
|
60
|
+
)
|
|
61
|
+
end
|
|
132
62
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::AbstractAdapter)
|
|
63
|
+
unless ::Rails.version < "7.1"
|
|
64
|
+
::ActiveRecord::ConnectionAdapters::ConnectionHandler.prepend(ActiveRecord::ConnectionHandler)
|
|
65
|
+
end
|
|
133
66
|
::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ActiveRecord::ConnectionPool)
|
|
134
67
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::QueryCache)
|
|
135
68
|
::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter)
|
|
136
69
|
|
|
137
70
|
::ActiveRecord::DatabaseConfigurations.prepend(ActiveRecord::DatabaseConfigurations)
|
|
138
|
-
::ActiveRecord::DatabaseConfigurations::DatabaseConfig.prepend(
|
|
71
|
+
::ActiveRecord::DatabaseConfigurations::DatabaseConfig.prepend(
|
|
72
|
+
ActiveRecord::DatabaseConfigurations::DatabaseConfig
|
|
73
|
+
)
|
|
139
74
|
|
|
140
75
|
::ActiveRecord::LogSubscriber.prepend(ActiveRecord::LogSubscriber)
|
|
141
76
|
::ActiveRecord::Migration.prepend(ActiveRecord::Migration)
|
|
@@ -155,8 +90,9 @@ module Switchman
|
|
|
155
90
|
::ActiveRecord::Relation.include(ActiveRecord::SpawnMethods)
|
|
156
91
|
::ActiveRecord::Relation.include(CallSuper)
|
|
157
92
|
|
|
158
|
-
::ActiveRecord::PredicateBuilder::
|
|
159
|
-
|
|
93
|
+
::ActiveRecord::PredicateBuilder::PolymorphicArrayValue.prepend(
|
|
94
|
+
ActiveRecord::PredicateBuilder::PolymorphicArrayValue
|
|
95
|
+
)
|
|
160
96
|
|
|
161
97
|
::ActiveRecord::Tasks::DatabaseTasks.singleton_class.prepend(ActiveRecord::Tasks::DatabaseTasks)
|
|
162
98
|
|
|
@@ -165,49 +101,66 @@ module Switchman
|
|
|
165
101
|
::ActiveRecord::TypeCaster::Map.include(ActiveRecord::TypeCaster::Map)
|
|
166
102
|
::ActiveRecord::TypeCaster::Connection.include(ActiveRecord::TypeCaster::Connection)
|
|
167
103
|
|
|
168
|
-
::Rails.singleton_class.prepend(Rails::ClassMethods)
|
|
169
|
-
|
|
170
104
|
::Arel::Table.prepend(Arel::Table)
|
|
171
105
|
::Arel::Visitors::ToSql.prepend(Arel::Visitors::ToSql)
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
106
|
|
|
175
|
-
def self.foreign_key_check(name, type, limit: nil)
|
|
176
|
-
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
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
initializer 'switchman.extend_connection_adapters', after: 'active_record.initialize_database' do
|
|
180
|
-
::ActiveSupport.on_load(:active_record) do
|
|
181
107
|
::ActiveRecord::ConnectionAdapters::AbstractAdapter.descendants.each do |klass|
|
|
182
108
|
klass.prepend(ActiveRecord::AbstractAdapter::ForeignKeyCheck)
|
|
183
109
|
end
|
|
184
110
|
|
|
185
|
-
require 'switchman/active_record/table_definition'
|
|
186
111
|
::ActiveRecord::ConnectionAdapters::TableDefinition.prepend(ActiveRecord::TableDefinition)
|
|
187
|
-
|
|
188
|
-
Shard.send(:initialize_sharding)
|
|
189
112
|
end
|
|
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
|
|
115
|
+
::ActiveRecord::Base
|
|
190
116
|
end
|
|
191
117
|
|
|
192
|
-
initializer
|
|
193
|
-
::ActiveSupport.on_load(:
|
|
194
|
-
|
|
195
|
-
require 'active_record/base'
|
|
118
|
+
initializer "switchman.error_patch", after: "active_record.initialize_database" do
|
|
119
|
+
::ActiveSupport.on_load(:active_record) do
|
|
120
|
+
::StandardError.include(StandardError)
|
|
196
121
|
end
|
|
197
122
|
end
|
|
198
123
|
|
|
199
|
-
initializer
|
|
200
|
-
::ActiveSupport.
|
|
201
|
-
require 'switchman/guard_rail'
|
|
124
|
+
initializer "switchman.initialize_cache", before: :initialize_cache, after: "active_record.initialize_database" do
|
|
125
|
+
::ActiveSupport::Cache.singleton_class.prepend(ActiveSupport::Cache::ClassMethods)
|
|
202
126
|
|
|
203
|
-
|
|
127
|
+
# if we haven't already setup our cache map out-of-band, set it up from
|
|
128
|
+
# config.cache_store now. behaves similarly to Rails' default
|
|
129
|
+
# initialize_cache initializer, but for each value in the map, rather
|
|
130
|
+
# than just Rails.cache. if config.cache_store is a flat value, uses it
|
|
131
|
+
# to fill just the Rails.env entry in the cache map.
|
|
132
|
+
unless Switchman.config[:cache_map].present?
|
|
133
|
+
cache_store_config = ::Rails.configuration.cache_store
|
|
134
|
+
cache_store_config = { ::Rails.env => cache_store_config } unless cache_store_config.is_a?(Hash)
|
|
135
|
+
|
|
136
|
+
Switchman.config[:cache_map] = ::ActiveSupport::Cache.lookup_stores(cache_store_config)
|
|
204
137
|
end
|
|
205
|
-
end
|
|
206
138
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
139
|
+
# if the configured cache map (either from before, or as populated from
|
|
140
|
+
# config.cache_store) didn't have an entry for Rails.env, add one using
|
|
141
|
+
# lookup_store(nil); matches the behavior of Rails' default
|
|
142
|
+
# initialize_cache initializer when config.cache_store is nil.
|
|
143
|
+
unless Switchman.config[:cache_map].key?(::Rails.env)
|
|
144
|
+
value = ::ActiveSupport::Cache.lookup_store(nil)
|
|
145
|
+
Switchman.config[:cache_map][::Rails.env] = value
|
|
146
|
+
end
|
|
210
147
|
|
|
148
|
+
middlewares = Switchman.config[:cache_map].values.filter_map do |store|
|
|
149
|
+
store.middleware if store.respond_to?(:middleware)
|
|
150
|
+
end.uniq
|
|
151
|
+
middlewares.each do |middleware|
|
|
152
|
+
config.middleware.insert_before("Rack::Runtime", middleware)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# prevent :initialize_cache from trying to (or needing to) set
|
|
156
|
+
# Rails.cache. once our switchman.extend_ar initializer (below) runs
|
|
157
|
+
# Rails.cache will be overridden to pull appropriate values from the
|
|
158
|
+
# cache map, but between now and then, Rails.cache should return the
|
|
159
|
+
# Rails.env entry in the cache map.
|
|
160
|
+
::Rails.cache = Switchman.config[:cache_map][::Rails.env]
|
|
161
|
+
::Rails.singleton_class.prepend(Rails::ClassMethods)
|
|
162
|
+
|
|
163
|
+
::ActiveSupport.on_load(:action_controller) do
|
|
211
164
|
::ActionController::Base.include(ActionController::Caching)
|
|
212
165
|
end
|
|
213
166
|
end
|
|
@@ -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
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Switchman
|
|
4
|
-
|
|
4
|
+
module Errors
|
|
5
|
+
class ManuallyCreatedShadowRecordError < RuntimeError
|
|
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."
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
def initialize(msg = DEFAULT_MSG)
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class NonExistentShardError < RuntimeError; end
|
|
15
|
+
|
|
16
|
+
class ParallelShardExecError < RuntimeError; end
|
|
17
|
+
|
|
18
|
+
class ShadowRecordError < RuntimeError; end
|
|
19
|
+
|
|
20
|
+
class UnshardedTableError < RuntimeError; end
|
|
21
|
+
end
|
|
7
22
|
end
|
|
@@ -5,21 +5,19 @@ 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|
|
|
16
|
+
arg_params = (RUBY_VERSION <= "2.8") ? "*args" : "*args, **kwargs"
|
|
15
17
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
|
16
|
-
def #{method}(
|
|
17
|
-
db = Shard.current(
|
|
18
|
-
|
|
19
|
-
db.unguard { super }
|
|
20
|
-
else
|
|
21
|
-
super
|
|
22
|
-
end
|
|
18
|
+
def #{method}(#{arg_params})
|
|
19
|
+
db = Shard.current(connection_class_for_self).database_server
|
|
20
|
+
db.unguard { super }
|
|
23
21
|
end
|
|
24
22
|
RUBY
|
|
25
23
|
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)
|
|
@@ -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,14 +66,17 @@ 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
|
+
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
|
-
puts
|
|
79
|
+
puts "Tearing down sharding for all specs"
|
|
77
80
|
@@shard1.database_server.destroy unless @@shard1.database_server == Shard.default.database_server
|
|
78
81
|
unless @@keep_the_shards
|
|
79
82
|
@@shard1.drop_database
|
|
@@ -92,7 +95,7 @@ module Switchman
|
|
|
92
95
|
dup = @@default_shard.dup
|
|
93
96
|
dup.id = @@default_shard.id
|
|
94
97
|
dup.save!
|
|
95
|
-
Switchman.cache.delete(
|
|
98
|
+
Switchman.cache.delete("default_shard")
|
|
96
99
|
Shard.default(reload: true)
|
|
97
100
|
dup = @@shard1.dup
|
|
98
101
|
dup.id = @@shard1.id
|
|
@@ -104,7 +107,7 @@ module Switchman
|
|
|
104
107
|
end
|
|
105
108
|
|
|
106
109
|
klass.before do
|
|
107
|
-
raise
|
|
110
|
+
raise "Sharding did not set up correctly" if @@sharding_failed
|
|
108
111
|
|
|
109
112
|
Shard.clear_cache
|
|
110
113
|
if use_transactional_tests
|
|
@@ -118,7 +121,7 @@ module Switchman
|
|
|
118
121
|
next if @@sharding_failed
|
|
119
122
|
|
|
120
123
|
# clean up after specs
|
|
121
|
-
DatabaseServer.
|
|
124
|
+
DatabaseServer.each do |ds|
|
|
122
125
|
if ds.fake? && ds != @shard2.database_server
|
|
123
126
|
ds.shards.delete_all unless use_transactional_tests
|
|
124
127
|
ds.destroy
|
|
@@ -129,7 +132,7 @@ module Switchman
|
|
|
129
132
|
klass.after(:all) do
|
|
130
133
|
# Don't truncate because that can create some fun cross-connection lock contention
|
|
131
134
|
Shard.delete_all
|
|
132
|
-
Switchman.cache.delete(
|
|
135
|
+
Switchman.cache.delete("default_shard")
|
|
133
136
|
Shard.default(reload: true)
|
|
134
137
|
end
|
|
135
138
|
end
|
data/lib/switchman/rails.rb
CHANGED
|
@@ -4,16 +4,13 @@ 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
|
|
14
11
|
|
|
15
12
|
def cache
|
|
16
|
-
Switchman::Shard.current
|
|
13
|
+
Switchman::Shard.current.database_server.cache_store
|
|
17
14
|
end
|
|
18
15
|
end
|
|
19
16
|
end
|