Banco lento
Pool de conexões: recicle com jitter
Todo pool de conexões descarta e recria conexões depois de um tempo de vida
máximo — MaxConnLifetime no pgx, pool_recycle no SQLAlchemy,
maxLifetimeSeconds no pg do Node. Se todas as conexões nasceram juntas no
boot do app, todas expiram no mesmo segundo. Um pool pequeno pode travar
nessa reconexão em massa e não se recuperar sozinho: o app fica respondendo
db ping failed indefinidamente, mesmo com o banco saudável.
Foi exatamente isso que tirou três apps do ar por cerca de 21 horas em junho de 2026. O banco estava de pé o tempo todo; o que travou foi o pool do lado do app.
A correção é espalhar a expiração (jitter). Como fazer em cada biblioteca:
Go — pgx/v5 (pgxpool)
cfg, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL"))
if err != nil {
return err
}
cfg.MaxConnLifetime = time.Hour
cfg.MaxConnLifetimeJitter = 30 * time.Minute // metade do tempo de vida
pool, err := pgxpool.NewWithConfig(ctx, cfg)Atenção: pgxpool.New(ctx, dsn) não aceita jitter, porque a DSN não tem como
expressar esse campo. Use ParseConfig + NewWithConfig como acima.
Se você usa github.com/guilhermebr/gox/postgres, atualize para a versão de
setembro de 2026 ou mais nova — o jitter passa a ser aplicado sozinho.
Python — psycopg_pool e SQLAlchemy
# psycopg_pool: max_lifetime já aplica um jitter aleatório por conexão
pool = ConnectionPool(dsn, max_lifetime=3600)
# SQLAlchemy: recicle e valide antes de usar
engine = create_engine(dsn, pool_recycle=3600, pool_pre_ping=True)O pool_pre_ping descarta conexões mortas antes de entregá-las ao seu código,
o que evita a maior parte dos sintomas mesmo quando a expiração é sincronizada.
Node — pg (node-postgres)
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
maxLifetimeSeconds: 3600,
idleTimeoutMillis: 30000,
});A partir do pg 8.10 a expiração é avaliada por conexão, não em lote.
Sintoma clássico
- O app responde
healthz: db ping failed ... context deadline exceededpor minutos ou horas. - O banco está saudável e aceita conexões novas normalmente.
- Um
restartdo app resolve na hora — e o problema volta algumas horas depois, sempre perto de uma marca redonda de tempo desde o último boot.
Se isso descreve o seu caso, o pool é o suspeito, não o banco.
Checklist rápido
- Sua app usa connection pool? (
pgxpool,psycopg_pool,pg.Pool— não abra uma conexão por request) - O tempo de vida das conexões tem jitter? (seção acima)
- Você bateu o limite de conexões do plano? Veja Bancos → Limites
- Os logs mostram timeout em alguma query específica?
- Tem
EXPLAIN ANALYZEda query suspeita?