switchman 3.0.5 → 4.0.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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +16 -15
  3. data/db/migrate/20180828183945_add_default_shard_index.rb +2 -2
  4. data/db/migrate/20180828192111_add_timestamps_to_shards.rb +1 -1
  5. data/db/migrate/20190114212900_add_unique_name_indexes.rb +10 -4
  6. data/lib/switchman/action_controller/caching.rb +2 -2
  7. data/lib/switchman/active_record/abstract_adapter.rb +6 -15
  8. data/lib/switchman/active_record/associations.rb +331 -0
  9. data/lib/switchman/active_record/attribute_methods.rb +182 -77
  10. data/lib/switchman/active_record/base.rb +249 -46
  11. data/lib/switchman/active_record/calculations.rb +98 -44
  12. data/lib/switchman/active_record/connection_handler.rb +18 -0
  13. data/lib/switchman/active_record/connection_pool.rb +27 -28
  14. data/lib/switchman/active_record/database_configurations.rb +44 -6
  15. data/lib/switchman/active_record/finder_methods.rb +46 -16
  16. data/lib/switchman/active_record/log_subscriber.rb +11 -5
  17. data/lib/switchman/active_record/migration.rb +52 -5
  18. data/lib/switchman/active_record/model_schema.rb +1 -1
  19. data/lib/switchman/active_record/pending_migration_connection.rb +17 -0
  20. data/lib/switchman/active_record/persistence.rb +37 -2
  21. data/lib/switchman/active_record/postgresql_adapter.rb +12 -11
  22. data/lib/switchman/active_record/predicate_builder.rb +2 -2
  23. data/lib/switchman/active_record/query_cache.rb +49 -20
  24. data/lib/switchman/active_record/query_methods.rb +202 -136
  25. data/lib/switchman/active_record/reflection.rb +1 -1
  26. data/lib/switchman/active_record/relation.rb +40 -28
  27. data/lib/switchman/active_record/spawn_methods.rb +2 -2
  28. data/lib/switchman/active_record/statement_cache.rb +11 -7
  29. data/lib/switchman/active_record/table_definition.rb +1 -1
  30. data/lib/switchman/active_record/tasks/database_tasks.rb +6 -1
  31. data/lib/switchman/active_record/test_fixtures.rb +53 -0
  32. data/lib/switchman/active_support/cache.rb +25 -4
  33. data/lib/switchman/arel.rb +45 -7
  34. data/lib/switchman/call_super.rb +2 -2
  35. data/lib/switchman/database_server.rb +123 -79
  36. data/lib/switchman/default_shard.rb +14 -5
  37. data/lib/switchman/engine.rb +79 -131
  38. data/lib/switchman/environment.rb +2 -2
  39. data/lib/switchman/errors.rb +17 -2
  40. data/lib/switchman/guard_rail/relation.rb +7 -10
  41. data/lib/switchman/guard_rail.rb +5 -0
  42. data/lib/switchman/parallel.rb +68 -0
  43. data/lib/switchman/r_spec_helper.rb +17 -28
  44. data/lib/switchman/rails.rb +1 -4
  45. data/{app/models → lib}/switchman/shard.rb +226 -241
  46. data/lib/switchman/sharded_instrumenter.rb +3 -3
  47. data/lib/switchman/shared_schema_cache.rb +11 -0
  48. data/lib/switchman/standard_error.rb +15 -12
  49. data/lib/switchman/test_helper.rb +2 -2
  50. data/{app/models → lib}/switchman/unsharded_record.rb +1 -1
  51. data/lib/switchman/version.rb +1 -1
  52. data/lib/switchman.rb +44 -12
  53. data/lib/tasks/switchman.rake +101 -54
  54. metadata +50 -58
  55. data/lib/switchman/active_record/association.rb +0 -206
  56. data/lib/switchman/open4.rb +0 -80
@@ -5,104 +5,23 @@ 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
- config.autoload_once_paths << File.expand_path('app/models', config.paths.path)
11
+ ::GuardRail.singleton_class.prepend(GuardRail::ClassMethods)
12
12
 
13
- def self.lookup_stores(cache_store_config)
14
- result = {}
15
- cache_store_config.each do |key, value|
16
- next if value.is_a?(String)
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]
67
- end
68
-
69
- initializer 'switchman.extend_ar', before: 'active_record.initialize_database' do
13
+ initializer "switchman.active_record_patch",
14
+ before: "active_record.initialize_database",
15
+ after: :setup_once_autoloader do
70
16
  ::ActiveSupport.on_load(:active_record) do
71
- require 'switchman/active_record/abstract_adapter'
72
- require 'switchman/active_record/association'
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/arel'
94
- require 'switchman/call_super'
95
- require 'switchman/rails'
96
- require 'switchman/guard_rail/relation'
97
- require 'switchman/standard_error'
98
-
99
- ::StandardError.include(StandardError)
17
+ # Switchman requires postgres, so just always load the pg adapter
18
+ require "active_record/connection_adapters/postgresql_adapter"
100
19
 
101
20
  self.default_shard = ::Rails.env.to_sym
102
21
  self.default_role = :primary
103
22
 
104
- include ActiveRecord::Base
105
- include ActiveRecord::AttributeMethods
23
+ prepend ActiveRecord::Base
24
+ prepend ActiveRecord::AttributeMethods
106
25
  include ActiveRecord::Persistence
107
26
  singleton_class.prepend ActiveRecord::ModelSchema::ClassMethods
108
27
 
@@ -111,25 +30,34 @@ module Switchman
111
30
  ::ActiveRecord::StatementCache::BindMap.prepend(ActiveRecord::StatementCache::BindMap)
112
31
  ::ActiveRecord::StatementCache::Substitute.send(:attr_accessor, :primary, :sharded)
113
32
 
114
- ::ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecord::CollectionAssociation)
115
- ::ActiveRecord::Associations::HasOneAssociation.prepend(ActiveRecord::ForeignAssociation)
116
- ::ActiveRecord::Associations::HasManyAssociation.prepend(ActiveRecord::ForeignAssociation)
33
+ ::ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecord::Associations::CollectionAssociation)
34
+ ::ActiveRecord::Associations::HasOneAssociation.prepend(ActiveRecord::Associations::ForeignAssociation)
35
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(ActiveRecord::Associations::ForeignAssociation)
117
36
 
118
37
  ::ActiveRecord::PredicateBuilder.singleton_class.prepend(ActiveRecord::PredicateBuilder)
119
38
 
120
- prepend(ActiveRecord::AutosaveAssociation)
39
+ prepend(ActiveRecord::Associations::AutosaveAssociation)
121
40
 
122
- ::ActiveRecord::Associations::Association.prepend(ActiveRecord::Association)
123
- ::ActiveRecord::Associations::BelongsToAssociation.prepend(ActiveRecord::BelongsToAssociation)
124
- ::ActiveRecord::Associations::CollectionProxy.include(ActiveRecord::CollectionProxy)
41
+ ::ActiveRecord::Associations::Association.prepend(ActiveRecord::Associations::Association)
42
+ ::ActiveRecord::Associations::BelongsToAssociation.prepend(ActiveRecord::Associations::BelongsToAssociation)
43
+ ::ActiveRecord::Associations::CollectionProxy.include(ActiveRecord::Associations::CollectionProxy)
125
44
 
126
- ::ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecord::Preloader::Association)
45
+ ::ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecord::Associations::Preloader::Association)
46
+ ::ActiveRecord::Associations::Preloader::Association::LoaderRecords.prepend(
47
+ ActiveRecord::Associations::Preloader::Association::LoaderRecords
48
+ )
127
49
  ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::AbstractAdapter)
50
+ unless ::Rails.version < "7.1"
51
+ ::ActiveRecord::ConnectionAdapters::ConnectionHandler.prepend(ActiveRecord::ConnectionHandler)
52
+ end
128
53
  ::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ActiveRecord::ConnectionPool)
129
54
  ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::QueryCache)
55
+ ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter)
130
56
 
131
57
  ::ActiveRecord::DatabaseConfigurations.prepend(ActiveRecord::DatabaseConfigurations)
132
- ::ActiveRecord::DatabaseConfigurations::DatabaseConfig.prepend(ActiveRecord::DatabaseConfigurations::DatabaseConfig)
58
+ ::ActiveRecord::DatabaseConfigurations::DatabaseConfig.prepend(
59
+ ActiveRecord::DatabaseConfigurations::DatabaseConfig
60
+ )
133
61
 
134
62
  ::ActiveRecord::LogSubscriber.prepend(ActiveRecord::LogSubscriber)
135
63
  ::ActiveRecord::Migration.prepend(ActiveRecord::Migration)
@@ -137,6 +65,11 @@ module Switchman
137
65
  ::ActiveRecord::MigrationContext.prepend(ActiveRecord::MigrationContext)
138
66
  ::ActiveRecord::Migrator.prepend(ActiveRecord::Migrator)
139
67
 
68
+ if ::Rails.version > "7.1.3"
69
+ ::ActiveRecord::PendingMigrationConnection.singleton_class
70
+ .include(ActiveRecord::PendingMigrationConnection::ClassMethods)
71
+ end
72
+
140
73
  ::ActiveRecord::Reflection::AbstractReflection.include(ActiveRecord::Reflection::AbstractReflection)
141
74
  ::ActiveRecord::Reflection::AssociationReflection.prepend(ActiveRecord::Reflection::AssociationScopeCache)
142
75
  ::ActiveRecord::Reflection::ThroughReflection.prepend(ActiveRecord::Reflection::AssociationScopeCache)
@@ -149,62 +82,77 @@ module Switchman
149
82
  ::ActiveRecord::Relation.include(ActiveRecord::SpawnMethods)
150
83
  ::ActiveRecord::Relation.include(CallSuper)
151
84
 
152
- ::ActiveRecord::PredicateBuilder::AssociationQueryValue.prepend(ActiveRecord::PredicateBuilder::AssociationQueryValue)
153
- ::ActiveRecord::PredicateBuilder::PolymorphicArrayValue.prepend(ActiveRecord::PredicateBuilder::AssociationQueryValue)
85
+ ::ActiveRecord::PredicateBuilder::PolymorphicArrayValue.prepend(
86
+ ActiveRecord::PredicateBuilder::PolymorphicArrayValue
87
+ )
154
88
 
155
89
  ::ActiveRecord::Tasks::DatabaseTasks.singleton_class.prepend(ActiveRecord::Tasks::DatabaseTasks)
156
90
 
91
+ ::ActiveRecord::TestFixtures.prepend(ActiveRecord::TestFixtures)
92
+
157
93
  ::ActiveRecord::TypeCaster::Map.include(ActiveRecord::TypeCaster::Map)
158
94
  ::ActiveRecord::TypeCaster::Connection.include(ActiveRecord::TypeCaster::Connection)
159
95
 
160
- ::Rails.singleton_class.prepend(Rails::ClassMethods)
161
-
162
96
  ::Arel::Table.prepend(Arel::Table)
163
97
  ::Arel::Visitors::ToSql.prepend(Arel::Visitors::ToSql)
164
- end
165
- end
166
-
167
- def self.foreign_key_check(name, type, limit: nil)
168
- 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
169
- end
170
98
 
171
- initializer 'switchman.extend_connection_adapters', after: 'active_record.initialize_database' do
172
- ::ActiveSupport.on_load(:active_record) do
173
99
  ::ActiveRecord::ConnectionAdapters::AbstractAdapter.descendants.each do |klass|
174
100
  klass.prepend(ActiveRecord::AbstractAdapter::ForeignKeyCheck)
175
101
  end
176
102
 
177
- require 'switchman/active_record/table_definition'
178
103
  ::ActiveRecord::ConnectionAdapters::TableDefinition.prepend(ActiveRecord::TableDefinition)
179
-
180
- if defined?(::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
181
- require 'switchman/active_record/postgresql_adapter'
182
- ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter)
183
- end
184
-
185
- Shard.send(:initialize_sharding)
186
104
  end
105
+ # Ensure that ActiveRecord::Base is always loaded before any app-level
106
+ # initializers can go try to load Switchman::Shard or we get a loop
107
+ ::ActiveRecord::Base
187
108
  end
188
109
 
189
- initializer 'switchman.eager_load' do
190
- ::ActiveSupport.on_load(:before_eager_load) do
191
- # This needs to be loaded before Switchman::Shard, otherwise it won't autoload it correctly
192
- require 'active_record/base'
110
+ initializer "switchman.error_patch", after: "active_record.initialize_database" do
111
+ ::ActiveSupport.on_load(:active_record) do
112
+ ::StandardError.include(StandardError)
193
113
  end
194
114
  end
195
115
 
196
- initializer 'switchman.extend_guard_rail', before: 'switchman.extend_ar' do
197
- ::ActiveSupport.on_load(:active_record) do
198
- require 'switchman/guard_rail'
116
+ initializer "switchman.initialize_cache", before: :initialize_cache, after: "active_record.initialize_database" do
117
+ ::ActiveSupport::Cache.singleton_class.prepend(ActiveSupport::Cache::ClassMethods)
118
+
119
+ # if we haven't already setup our cache map out-of-band, set it up from
120
+ # config.cache_store now. behaves similarly to Rails' default
121
+ # initialize_cache initializer, but for each value in the map, rather
122
+ # than just Rails.cache. if config.cache_store is a flat value, uses it
123
+ # to fill just the Rails.env entry in the cache map.
124
+ unless Switchman.config[:cache_map].present?
125
+ cache_store_config = ::Rails.configuration.cache_store
126
+ cache_store_config = { ::Rails.env => cache_store_config } unless cache_store_config.is_a?(Hash)
199
127
 
200
- ::GuardRail.singleton_class.prepend(GuardRail::ClassMethods)
128
+ Switchman.config[:cache_map] = ::ActiveSupport::Cache.lookup_stores(cache_store_config)
201
129
  end
202
- end
203
130
 
204
- initializer 'switchman.extend_controller', after: 'guard_rail.extend_ar' do
205
- ::ActiveSupport.on_load(:action_controller) do
206
- require 'switchman/action_controller/caching'
131
+ # if the configured cache map (either from before, or as populated from
132
+ # config.cache_store) didn't have an entry for Rails.env, add one using
133
+ # lookup_store(nil); matches the behavior of Rails' default
134
+ # initialize_cache initializer when config.cache_store is nil.
135
+ unless Switchman.config[:cache_map].key?(::Rails.env)
136
+ value = ::ActiveSupport::Cache.lookup_store(nil)
137
+ Switchman.config[:cache_map][::Rails.env] = value
138
+ end
139
+
140
+ middlewares = Switchman.config[:cache_map].values.filter_map do |store|
141
+ store.middleware if store.respond_to?(:middleware)
142
+ end.uniq
143
+ middlewares.each do |middleware|
144
+ config.middleware.insert_before("Rack::Runtime", middleware)
145
+ end
207
146
 
147
+ # prevent :initialize_cache from trying to (or needing to) set
148
+ # Rails.cache. once our switchman.extend_ar initializer (below) runs
149
+ # Rails.cache will be overridden to pull appropriate values from the
150
+ # cache map, but between now and then, Rails.cache should return the
151
+ # Rails.env entry in the cache map.
152
+ ::Rails.cache = Switchman.config[:cache_map][::Rails.env]
153
+ ::Rails.singleton_class.prepend(Rails::ClassMethods)
154
+
155
+ ::ActiveSupport.on_load(:action_controller) do
208
156
  ::ActionController::Base.include(ActionController::Caching)
209
157
  end
210
158
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'etc'
3
+ require "etc"
4
4
 
5
5
  module Switchman
6
6
  class Environment
7
- def self.cpu_count(nproc_bin = 'nproc')
7
+ def self.cpu_count(nproc_bin = "nproc")
8
8
  return Etc.nprocessors if Etc.respond_to?(:nprocessors)
9
9
 
10
10
  `#{nproc_bin}`.to_i
@@ -1,7 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Switchman
4
- class NonExistentShardError < RuntimeError; end
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
- class ParallelShardExecError < RuntimeError; end
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,18 @@ module Switchman
5
5
  module Relation
6
6
  def exec_queries(*args)
7
7
  if lock_value
8
- db = Shard.current(connection_classes).database_server
9
- return db.unguard { super } if ::GuardRail.environment != db.guard_rail_environment
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
- def #{method}(*args)
17
- db = Shard.current(connection_classes).database_server
18
- if ::GuardRail.environment != db.guard_rail_environment
19
- db.unguard { super }
20
- else
21
- super
22
- end
17
+ def #{method}(*args, **kwargs)
18
+ db = Shard.current(connection_class_for_self).database_server
19
+ db.unguard { super }
23
20
  end
24
21
  RUBY
25
22
  end
@@ -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 TransformingIO
54
+ delegate_missing_to :@original_io
55
+
56
+ def initialize(transformer, original_io)
57
+ @transformer = transformer
58
+ @original_io = original_io
59
+ end
60
+
61
+ def puts(*args)
62
+ args.flatten.each { |arg| @original_io.puts @transformer.call(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 'switchman/test_helper'
3
+ require "switchman/test_helper"
4
4
 
5
5
  module Switchman
6
6
  # including this module in your specs will give you several shards to
@@ -34,9 +34,9 @@ module Switchman
34
34
  groups = group.class.descendant_filtered_examples.map(&:example_group).uniq
35
35
  next unless groups.any? { |descendant_group| RSpecHelper.included_in?(descendant_group) }
36
36
 
37
- puts 'Setting up sharding for all specs...'
37
+ puts "Setting up sharding for all specs..."
38
38
  Shard.delete_all
39
- Switchman.cache.delete('default_shard')
39
+ Switchman.cache.delete("default_shard")
40
40
 
41
41
  @@shard1, @@shard2 = TestHelper.recreate_persistent_test_shards
42
42
  @@default_shard = Shard.default
@@ -48,7 +48,7 @@ module Switchman
48
48
  @@shard1 = @@shard1.create_new_shard
49
49
  @@shard2 = @@shard2.create_new_shard
50
50
  rescue => e
51
- warn 'Sharding setup FAILED!:'
51
+ warn "Sharding setup FAILED!:"
52
52
  while e
53
53
  warn "\n#{e}\n"
54
54
  warn e.backtrace
@@ -66,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('default_shard')
69
+ Switchman.cache.delete("default_shard")
70
70
  Shard.default(reload: true)
71
- puts 'Done!'
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 'Tearing down sharding for all specs'
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('default_shard')
98
+ Switchman.cache.delete("default_shard")
96
99
  Shard.default(reload: true)
97
100
  dup = @@shard1.dup
98
101
  dup.id = @@shard1.id
@@ -104,47 +107,33 @@ module Switchman
104
107
  end
105
108
 
106
109
  klass.before do
107
- raise 'Sharding did not set up correctly' if @@sharding_failed
110
+ raise "Sharding did not set up correctly" if @@sharding_failed
108
111
 
109
112
  Shard.clear_cache
110
113
  if use_transactional_tests
111
114
  Shard.default(reload: true)
112
115
  @shard1 = Shard.find(@shard1.id)
113
116
  @shard2 = Shard.find(@shard2.id)
114
- shards = [@shard2]
115
- shards << @shard1 unless @shard1.database_server == Shard.default.database_server
116
- shards.each do |shard|
117
- shard.activate do
118
- ::ActiveRecord::Base.connection.begin_transaction joinable: false
119
- end
120
- end
121
117
  end
122
118
  end
123
119
 
124
120
  klass.after do
125
121
  next if @@sharding_failed
126
122
 
127
- if use_transactional_tests
128
- shards = [@shard2]
129
- shards << @shard1 unless @shard1.database_server == Shard.default.database_server
130
- shards.each do |shard|
131
- shard.activate do
132
- ::ActiveRecord::Base.connection.rollback_transaction if ::ActiveRecord::Base.connection.transaction_open?
133
- end
134
- end
135
- end
136
123
  # clean up after specs
137
- DatabaseServer.all.each do |ds|
124
+ DatabaseServer.each do |ds|
138
125
  if ds.fake? && ds != @shard2.database_server
139
126
  ds.shards.delete_all unless use_transactional_tests
140
127
  ds.destroy
141
128
  end
129
+ ds.remove_instance_variable(:@primary_shard_id) if ds.instance_variable_defined?(:@primary_shard_id)
142
130
  end
143
131
  end
144
132
 
145
133
  klass.after(:all) do
146
- Shard.connection.update("TRUNCATE #{Shard.quoted_table_name} CASCADE")
147
- Switchman.cache.delete('default_shard')
134
+ # Don't truncate because that can create some fun cross-connection lock contention
135
+ Shard.delete_all
136
+ Switchman.cache.delete("default_shard")
148
137
  Shard.default(reload: true)
149
138
  end
150
139
  end
@@ -4,10 +4,7 @@ module Switchman
4
4
  module Rails
5
5
  module ClassMethods
6
6
  def self.prepended(klass)
7
- # in Rails 4+, the Rails.cache= method was used during bootstrap to set
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