database_cleaner icon indicating copy to clipboard operation
database_cleaner copied to clipboard

ActiveRecord::ConnectionNotEstablished in random places

Open stiig opened this issue 1 year ago • 2 comments

Hi team, sometimes we have this error during running the tests

Randomized with seed 38777
..........................F
An error occurred in an `after(:context)` hook.
Failure/Error: DatabaseCleaner.clean_with(:truncation, except: %i[translations])

ActiveRecord::ConnectionNotEstablished:
  connection to server at "127.0.0.1", port 5432 failed: server closed the connection unexpectedly
  	This probably means the server terminated abnormally
  	before or while processing the request.
Randomized with seed 59497
...........*..................................................F
An error occurred in an `after(:context)` hook.
Failure/Error: DatabaseCleaner.clean_with(:truncation, except: %i[translations])

ActiveRecord::ConnectionNotEstablished:
  connection to server at "127.0.0.1", port 5432 failed: FATAL:  the database system is shutting down

we run them in parallel image and I have no idea where to find roots of the problem

I tried to set reaping_frequency: 0 setting but it didn't help (logs from runs with this setting)

Versions:

  • database_cleaner-active_record (2.1.0)
  • database_cleaner-core (2.0.1)
  • rails (7.1.3.4)
  • rspec-core (3.13.0)

Tests crash in random places and often a restart helps, we don't have something special for database cleaner, our config: rails_helper.rb:

  config.before(:suite) do
    DatabaseCleaner.strategy = :transaction
  end

  config.around do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end

  config.after(:all) do
    DatabaseCleaner.clean_with(:truncation, except: %i[translations])
  end

stiig avatar Sep 03 '24 08:09 stiig

We're hitting this too on CI; Haven't hit it locally yet to debug further

Database cleaner boilerplate with Cucumber tests:

preserved_tables = %w[ ... ]
DatabaseCleaner.strategy = :deletion, {:except => preserved_tables}
Cucumber::Rails::Database.autorun_database_cleaner = false
Before('not @no-database-cleaner') do
  $stderr.puts "[database-cleaner] start"
  DatabaseCleaner.start
end

After('not @no-database-cleaner') do
  $stderr.puts "[database-cleaner] clean"
  DatabaseCleaner.clean
end

InstallPlugin do |config, registry|
  DatabaseCleaner.clean_with :truncation, {:except => preserved_tables}
end

Error:

14:42:56   Scenario: Example scenario
# features/foo/example.feature:62
14:42:56 [database-cleaner] start
.... test completes ...
14:43:37 [database-cleaner] clean
14:43:37       connection is closed (ActiveRecord::ConnectionNotEstablished)
14:43:37       ./features/support/env.rb:32:in `After'

After this error occurs, the rest of the tests generally fail on either connection not established, or that database records already exist from the cleanup not working

14:43:38     Given I have a user "TestUser"                                                                              # features/steps.rb:8
14:43:38       Validation failed: Username has already been taken (ActiveRecord::RecordInvalid)

Versions:

database_cleaner-2.1.0
database_cleaner-active_record-2.1.0
activerecord-7.0.8.7
rails-7.0.8.7
cucumber-8.0.0
capybara-3.40.0
puma-6.4.3

We are also running a worker and another service connected to the same test database which is shared with our capybara browser test application, which might be related to the problem.

adfoster-r7 avatar Mar 20 '25 22:03 adfoster-r7

I've been running into this and assumed its because my connection setup is a bit manual, bisecting my suite properly I've found that its the memoisation of the connection wrapper that causes it for me, I'm on a legacy version of Rails for this project and the memoisation of the connection means it can be stale when accessed in a later test, simply checking for an open connection before cleaning solves it locally:

      def clean
        @connection = nil unless connection_class.connected?

        # ...
      end

For those encountering it I've patched this like this for now:

DatabaseCleaner::ActiveRecord::Truncation.class_eval do
  alias_method :__clean, :clean

  def clean
    @connection = nil unless connection_class.connected?
    __clean
  end
end

JonRowe avatar May 06 '25 10:05 JonRowe