Skip to content

Commit 1f98fd2

Browse files
committed
Implement ability to opt out of parallel database hooks
Preivously, you could append your own hooks to parallel tests, but there was no way to skip the Rails database parallelization hooks. There may be cases where you want to implement your own database handling, so this allows you to implement all of that in a `parallelize_setup` block without having Rails create any databases for you. To skip Rails database parallelization when running parallel tests you can set it in the `parallelize` caller: ```ruby parallelize(workers: 10, parallelize_databases: false) ``` Or in your application config: ```ruby config.active_support.parallelize_test_databases = false ``` This is set to true by default. Note: if you do not create databases for the processes, it is possible for your test suite to deadlock. If you're seeing frequent deadlocks, you may need to create more databases for the processes to use or adjust your tests/code so it's less likely to cause database contention.
1 parent 6814f71 commit 1f98fd2

File tree

6 files changed

+82
-3
lines changed

6 files changed

+82
-3
lines changed

activerecord/lib/active_record/test_databases.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@
55
module ActiveRecord
66
module TestDatabases # :nodoc:
77
ActiveSupport::Testing::Parallelization.before_fork_hook do
8-
ActiveRecord::Base.connection_handler.clear_all_connections!
8+
if ActiveSupport.parallelize_test_databases
9+
ActiveRecord::Base.connection_handler.clear_all_connections!
10+
end
911
end
1012

1113
ActiveSupport::Testing::Parallelization.after_fork_hook do |i|
12-
create_and_load_schema(i, env_name: ActiveRecord::ConnectionHandling::DEFAULT_ENV.call)
14+
if ActiveSupport.parallelize_test_databases
15+
create_and_load_schema(i, env_name: ActiveRecord::ConnectionHandling::DEFAULT_ENV.call)
16+
end
1317
end
1418

1519
def self.create_and_load_schema(i, env_name:)

activerecord/test/cases/test_databases_test.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,32 @@ def test_create_databases_after_fork
5353
ENV["RAILS_ENV"] = previous_env
5454
end
5555

56+
def test_create_databases_skipped_if_parallelize_test_databases_is_false
57+
parallelize_databases = ActiveSupport.parallelize_test_databases
58+
ActiveSupport.parallelize_test_databases = false
59+
60+
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "arunit"
61+
prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, {
62+
"arunit" => {
63+
"primary" => { "adapter" => "sqlite3", "database" => "test/db/primary.sqlite3" }
64+
}
65+
}
66+
67+
idx = 42
68+
base_db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
69+
expected_database = "#{base_db_config.database}"
70+
71+
ActiveSupport::Testing::Parallelization.after_fork_hooks.each { |cb| cb.call(idx) }
72+
73+
# In this case, there should be no updates
74+
assert_equal expected_database, ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary").database
75+
ensure
76+
ActiveSupport.parallelize_test_databases = parallelize_databases
77+
ActiveRecord::Base.configurations = prev_configs
78+
ActiveRecord::Base.establish_connection(:arunit)
79+
ENV["RAILS_ENV"] = previous_env
80+
end
81+
5682
def test_order_of_configurations_isnt_changed_by_test_databases
5783
previous_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "arunit"
5884
prev_configs, ActiveRecord::Base.configurations = ActiveRecord::Base.configurations, {

activesupport/CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
* Implement ability to skip creating parallel testing databases.
2+
3+
With parallel testing, Rails will create a database per process. If this isn't
4+
desirable or you would like to implement databases handling on your own, you can
5+
now turn off this default behavior.
6+
7+
To skip creating a database per process, you can change it via the
8+
`parallelize` method:
9+
10+
```ruby
11+
parallelize(workers: 10, parallelize_databases: false)
12+
```
13+
14+
or via the application configuration:
15+
16+
```ruby
17+
config.active_support.parallelize_databases = false
18+
```
19+
20+
*Eileen M. Uchitelle*
21+
122
* Allow to configure maximum cache key sizes
223

324
When the key exceeds the configured limit (250 bytes by default), it will be truncated and

activesupport/lib/active_support.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ def self.eager_load!
104104

105105
cattr_accessor :test_order # :nodoc:
106106
cattr_accessor :test_parallelization_threshold, default: 50 # :nodoc:
107+
cattr_accessor :parallelize_test_databases, default: true # :nodoc:
107108

108109
@error_reporter = ActiveSupport::ErrorReporter.new
109110
singleton_class.attr_accessor :error_reporter # :nodoc:

activesupport/lib/active_support/test_case.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,27 @@ def test_order
7979
# Because parallelization presents an overhead, it is only enabled when the
8080
# number of tests to run is above the +threshold+ param. The default value is
8181
# 50, and it's configurable via +config.active_support.test_parallelization_threshold+.
82-
def parallelize(workers: :number_of_processors, with: :processes, threshold: ActiveSupport.test_parallelization_threshold)
82+
#
83+
# If you want to skip Rails default creation of one database per process in favor of
84+
# writing your own implementation, you can set +parallelize_databases+, or configure it
85+
# via +config.active_support.parallelize_test_databases+.
86+
#
87+
# parallelize(workers: :number_of_processes, parallelize_databases: false)
88+
#
89+
# Note that your test suite may deadlock if you attempt to use only one database
90+
# with multiple processes.
91+
def parallelize(workers: :number_of_processors, with: :processes, threshold: ActiveSupport.test_parallelization_threshold, parallelize_databases: ActiveSupport.parallelize_test_databases)
8392
case
8493
when ENV["PARALLEL_WORKERS"]
8594
workers = ENV["PARALLEL_WORKERS"].to_i
8695
when workers == :number_of_processors
8796
workers = (Concurrent.available_processor_count || Concurrent.processor_count).floor
8897
end
8998

99+
if with == :processes
100+
ActiveSupport.parallelize_test_databases = parallelize_databases
101+
end
102+
90103
Minitest.parallel_executor = ActiveSupport::Testing::ParallelizeExecutor.new(size: workers, with: with, threshold: threshold)
91104
end
92105

railties/test/application/configuration_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3204,6 +3204,20 @@ class Post < ActiveRecord::Base
32043204
assert_equal 1234, ActiveSupport.test_parallelization_threshold
32053205
end
32063206

3207+
test "ActiveSupport.parallelize_test_databases can be configured via config.active_support.parallelize_test_databases" do
3208+
remove_from_config '.*config\.load_defaults.*\n'
3209+
3210+
app_file "config/environments/test.rb", <<-RUBY
3211+
Rails.application.configure do
3212+
config.active_support.parallelize_test_databases = false
3213+
end
3214+
RUBY
3215+
3216+
app "test"
3217+
3218+
assert_not ActiveSupport.parallelize_test_databases
3219+
end
3220+
32073221
test "custom serializers should be able to set via config.active_job.custom_serializers in an initializer" do
32083222
class ::DummySerializer < ActiveJob::Serializers::ObjectSerializer; end
32093223

0 commit comments

Comments
 (0)