ruby 80 lines · 3 tabs

Keep DB Connections Healthy in Long Jobs

Shared by codesnips Jan 2026
3 tabs
module ConnectionHealth
  extend ActiveSupport::Concern

  def with_fresh_connection
    conn = ActiveRecord::Base.connection
    conn.verify! # pings and reconnects if the socket is dead
    yield
  ensure
    ActiveRecord::Base.clear_active_connections!
  end

  def checkout_fresh
    conn = ActiveRecord::Base.connection
    conn.reconnect! unless safe?(conn)
    yield conn
  end

  private

  def safe?(conn)
    conn.active? && conn.verify!
  rescue ActiveRecord::ConnectionNotEstablished, PG::Error
    false
  end
end
3 files · ruby Explain with highlit

Long-running background jobs are the classic place where ActiveRecord's connection pool bites back. A worker might hold a checked-out connection for minutes while it does slow, non-database work (HTTP calls, file parsing, sleeping between batches), and in that window the connection can go stale: the server closes it after tcp_keepalives or a proxy like PgBouncer drops it, leaving the next query to fail with PG::ConnectionBad. This set of files shows how to keep connections healthy without leaking them out of the pool.

The ConnectionHealth concern in the first tab wraps a block in with_fresh_connection. It first calls verify! on the current connection, which pings the server and transparently reconnects if the socket is dead. Crucially it also uses clear_active_connections! after the work completes so the connection returns to the pool rather than being pinned to the thread for the whole job. The checkout_fresh helper is for the harder case where a connection must be discarded entirely: reconnect! forces a new socket even if active? optimistically reports true.

In ExportLargeReportJob, the pattern is applied to a batch loop. The job deliberately does NOT hold one connection across the whole run. Instead it processes with in_batches and, on every iteration, releases the connection during the slow upload_chunk step by wrapping only the query portion in with_fresh_connection. This means the pool stays available to other threads and each batch re-verifies its connection, so a mid-job network blip only costs one retry rather than failing the whole export.

The LongJobBase tab ties it together as a reusable superclass. around_perform-style logic reaps idle connections with ActiveRecord::Base.clear_active_connections! and rescues ActiveRecord::StatementInvalid and PG::Error to retry once with a fresh connection before giving up. The trade-off is subtle: verifying a connection adds a round-trip, so it is applied per batch rather than per row. Reaching for this pattern makes sense whenever a job runs longer than the database or pooler idle timeout, or when many workers contend for a small pool. The main pitfall is forgetting to release the connection, which silently exhausts the pool under load.


Related snips

Share this code

Here's the card — post it anywhere.

Keep DB Connections Healthy in Long Jobs — share card
Link copied