Banco de staging esgotava conexões sem PgBouncer
Nota histórica: este learning descreve o banco de staging quando ele era PostgreSQL gerenciado da DigitalOcean atrás de PgBouncer. Staging migrou para Aurora Serverless v2 em julho/2026 (spec) e os pools de PgBouncer não existem mais. O aprendizado sobre pressão de conexões continua válido.
O que aconteceu
O ambiente de staging ficava inacessível com o erro OperationalError: remaining connection slots are reserved for roles with SUPERUSER attribute. Qualquer requisição ao dashboard ou à API falhava.
Causa raiz
O banco de staging (db-s-1vcpu-1gb) suportava apenas ~25 conexões simultâneas. Sem PgBouncer, cada pod (web, worker, beat, worker-habits) mantém conexões persistentes abertas por thread. Com 4 deployments mais os pods extras criados durante o RollingUpdate, os slots se esgotavam e o Postgres reservava os últimos para o superusuário, rejeitando conexões da aplicação.
O entrypoint do commons injeta DATABASE_URL_FULLACCESS como DATABASE_URL em todos os pods de runtime — portanto, definir apenas CONN_MAX_AGE=0 no settings não seria suficiente por si só: a solução definitiva foi rotear todas as conexões da aplicação pelo PgBouncer.
Correção
Criados dois connection pools na DigitalOcean via Terraform:
onion— usuárioonion(fullaccess), modotransaction, tamanho 10onion-readonly— usuárioonion_readonly(readonly), modotransaction, tamanho 10
Os secrets DATABASE_URL_FULLACCESS e DATABASE_URL_READONLY no k8s foram atualizados para apontar para os pools (porta 25061). O migrate-job manteve conexão direta (porta 25060), porque o PgBouncer em modo transaction não suporta DDL.
Como evitar
- Sempre usar pooling de conexões para a aplicação em ambientes com banco pequeno
DATABASE_URL_FULLACCESSeDATABASE_URL_READONLYdevem sempre apontar para pools, nunca direto ao banco- A única exceção é o job de migrate, que deve sempre usar conexão direta — pooling em modo
transactionnão suporta DDL - Ao criar novos ambientes, provisionar os pools junto com o cluster no mesmo
database.tf