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ário onion (fullaccess), modo transaction, tamanho 10
  • onion-readonly — usuário onion_readonly (readonly), modo transaction, 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_FULLACCESS e DATABASE_URL_READONLY devem 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 transaction não suporta DDL
  • Ao criar novos ambientes, provisionar os pools junto com o cluster no mesmo database.tf

Referências