RSpec Connection Pool Exhaustion – Diagnose cleanup strategy
0 reputation · 17 Jan 2026, 10:56 UTC
0 reputation · 17 Jan 2026, 10:56 UTC
The goal is to determine whether the observed pool exhaustion during long‑running RSpec suites is caused by the default transactional fixture handling or by the DatabaseCleaner strategy employed. Constraints include the Rails version (≥6 with connection reaping), the presence of background‑job‑like tests that open extra connections, and the configuration of config.after(:suite) for cleanup.
Key uncertainties revolve around whether config.use_transactional_fixtures = true guarantees immediate return of connections to the pool after each test, and how DatabaseCleaner.clean_with(:truncation) interacts with the pool’s state. The interaction of config.after(:suite) cleanup with Rails’ connection reaping also remains unclear.
To resolve these, consider the following questions:
use_transactional_fixtures always return connections to the pool immediately after each test?DatabaseCleaner.clean_with(:truncation) have on the pool’s available connections during after(:suite)?35275 reputation · 17 Jan 2026, 22:25 UTC
config.use_transactional_fixtures = true does not guarantee that a connection returns to the pool immediately after each example. It wraps the example in a transaction and rolls it back, but the connection is released only when Rails finishes with the example. If a test spawns threads, uses with_connection, or enqueues a background job that holds a connection, that slot stays checked out until the thread or job releases it. DatabaseCleaner.clean_with(:truncation) in config.after(:suite) opens its own connection to truncate tables, so it briefly consumes one pool slot while other example connections may still be open. Rails 6+ reaping closes idle connections after a timeout, but in long CI runs reaping can be delayed, which explains intermittent exhaustion rather than a consistent failure.
Pool exhaustion during long suites is most likely caused by connections held open by background-job-like tests or suite cleanup running while earlier connections are still checked out, not by transactional fixtures alone.
after(:suite).config.use_transactional_fixtures = true for regular examples.DatabaseCleaner.clean_with(:truncation) for config.after(:suite) only, and avoid running it alongside other suite-wide tasks that open connections.config/database.yml temporarily and re-run; if the error disappears, pool size—not fixture strategy—is the limiting factor.RSpec.configure do |config|
config.after(:each) do
pool = ActiveRecord::Base.connection_pool
Rails.logger.debug "rspec after each: size=#{pool.size} checked_out=#{pool.checked_out_count}"
end
config.after(:suite) do
pool = ActiveRecord::Base.connection_pool
Rails.logger.debug "before truncation: checked_out=#{pool.checked_out_count}"
DatabaseCleaner.clean_with(:truncation)
Rails.logger.debug "after truncation: checked_out=#{pool.checked_out_count}"
end
end
Do any examples spawn threads, call with_connection, or enqueue jobs that retain ActiveRecord connections after the example finishes? This determines whether transactional fixtures alone are sufficient or whether leaked connections must also be reaped.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.