require "bundler/inline" gemfile(true) do source "https://rubygems.org" gem "rails", "~> 7.2" gem "pg" end require "active_record" require "logger" class ActiveRecord::ConnectionAdapters::ConnectionPool::ConnectionLeasingQueue private def internal_poll(timeout) result = super result rescue ActiveRecord::ConnectionTimeoutError => e puts "\n=== Connection Pool Timeout ===" puts "\nPool size: #{ActiveRecord::Base.connection_pool.size}" puts "Checked out connections: #{ActiveRecord::Base.connection_pool.connections.count}" connection_owners = ActiveRecord::Base.connection_pool.connections.map(&:owner) puts "\nThread list:" Thread.list.each_with_index do |t, i| connection_mark = connection_owners.include?(t) ? "[HAS CONNECTION]" : "[NO CONNECTION]" connection_mark += "[CURRENT]" if t == Thread.current connection_mark += "[MAIN]" if t == Thread.main puts "---- #{connection_mark} : Thread #{i}, status: #{t.status.inspect}, thread_info: #{t.inspect.split(' ', 2)[1].chop}" end puts "\nCurrent thread: #{Thread.current.inspect}" puts "\n=== End Debug Info ===\n" raise e end end # Configure a very small connection pool ActiveRecord::Base.establish_connection( adapter: "postgresql", database: "playground", # create a database named "playground" in your local PostgreSQL pool: 2, checkout_timeout: 2 ) ActiveRecord::Base.logger = Logger.new(STDOUT) # Create a simple table ActiveRecord::Schema.define do create_table :shipments, force: true do |t| t.string :status end end class Shipment < ActiveRecord::Base end def dangerous_threads threads = [] # We duplicate code to report different lines in the stack trace threads << Thread.new do puts "Thread 1 starting..." ActiveRecord::Base.connection.execute("select pg_sleep(5)") end threads << Thread.new do puts "Thread 2 starting..." ActiveRecord::Base.connection.execute("select pg_sleep(5)") end threads << Thread.new do puts "Thread 2 starting..." ActiveRecord::Base.connection.execute("select pg_sleep(5)") end threads.each(&:join) end puts "Connection pool size: #{ActiveRecord::Base.connection_pool.size}" puts "Starting threads..." dangerous_threads