Connection pooling com PgBouncer
Cada conexão no PostgreSQL consome ~5 MB de RAM e um processo dedicado. Com 200 conexões simultâneas de uma API Node.js, você está consumindo 1 GB só para gerenciar conexões — antes de processar qualquer query. PgBouncer multiplexia centenas de conexões da aplicação em poucas conexões reais ao PostgreSQL, resolvendo o "too many connections" e melhorando o throughput.
Instalar e configurar o PgBouncer
PgBouncer fica entre a aplicação e o PostgreSQL:
# Instalar PgBouncer
sudo apt install -y pgbouncer
# Configuração principal
sudo nano /etc/pgbouncer/pgbouncer.ini
[databases]
# Alias para o banco real (a aplicação conecta em "minha_db")
minha_db = host=127.0.0.1 port=5432 dbname=minha_db
[pgbouncer]
listen_addr = 127.0.0.1 # ou 0.0.0.0 para acesso remoto
listen_port = 6432 # porta do PgBouncer (diferente do PG: 5432)
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
# Modo de pooling
pool_mode = transaction # recomendado para APIs stateless
# Limites de conexão
max_client_conn = 1000 # máximo de clientes simultâneos
default_pool_size = 25 # conexões reais ao PostgreSQL por banco/usuário
min_pool_size = 5
reserve_pool_size = 5
reserve_pool_timeout = 3
# Logs
log_connections = 0
log_disconnections = 0
log_pooler_errors = 1
stats_period = 60Configurar autenticação de usuários
O PgBouncer precisa de lista de usuários e senhas:
# Gerar hash scram-sha-256 da senha (conectar ao PostgreSQL):
sudo -u postgres psql -c "SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'minha_app';"
# O hash começa com SCRAM-SHA-256$...
# Copiar para o userlist.txt:
sudo nano /etc/pgbouncer/userlist.txt
# Formato: "usuario" "hash_ou_senha"
"minha_app" "SCRAM-SHA-256$4096:...(hash completo)"
# Alternativa mais simples (menos seguro — texto plano):
"minha_app" "senha_da_aplicacao"
# Iniciar/reiniciar PgBouncer
sudo systemctl enable pgbouncer
sudo systemctl restart pgbouncer
# Testar conexão via PgBouncer (porta 6432):
psql -h 127.0.0.1 -p 6432 -U minha_app -d minha_db -c "SELECT 1"
# Verificar status do pool:
psql -h 127.0.0.1 -p 6432 -U minha_app pgbouncer -c "SHOW POOLS;"Monitorar o PgBouncer
O banco virtual pgbouncer expõe estatísticas em tempo real:
# Conectar ao banco de admin do PgBouncer:
psql -h 127.0.0.1 -p 6432 -U minha_app pgbouncer
-- Ver pools ativos:
SHOW POOLS;
-- cl_active: clientes com conexão ativa
-- cl_waiting: clientes aguardando conexão disponível
-- sv_active: conexões reais ao PostgreSQL em uso
-- sv_idle: conexões reais disponíveis no pool
-- Ver clientes conectados:
SHOW CLIENTS;
-- Ver servidores (conexões reais ao PostgreSQL):
SHOW SERVERS;
-- Ver estatísticas de performance:
SHOW STATS;
-- total_xact_count: número de transações processadas
-- total_query_count: número de queries
-- total_wait_time: tempo total aguardando conexão (microsegundos)
-- Recarregar config sem reiniciar:
RELOAD;
-- Ver configuração atual:
SHOW CONFIG;Atualizar a string de conexão da aplicação
A aplicação aponta para o PgBouncer em vez de conectar diretamente ao PostgreSQL:
# Antes (conecta direto no PostgreSQL):
DATABASE_URL=postgresql://minha_app:senha@localhost:5432/minha_db
# Depois (conecta no PgBouncer):
DATABASE_URL=postgresql://minha_app:senha@localhost:6432/minha_db
# ^^^^
# porta do PgBouncer
# Em Docker Compose — PgBouncer como serviço separado:
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: minha_db
POSTGRES_USER: minha_app
POSTGRES_PASSWORD: senha
pgbouncer:
image: edoburu/pgbouncer:latest
environment:
DATABASE_URL: postgresql://minha_app:senha@postgres:5432/minha_db
POOL_MODE: transaction
MAX_CLIENT_CONN: 1000
DEFAULT_POOL_SIZE: 25
ports:
- "6432:5432"
depends_on:
- postgres
app:
environment:
DATABASE_URL: postgresql://minha_app:senha@pgbouncer:5432/minha_dbSolucionar problemas comuns
Erros frequentes ao migrar para PgBouncer e suas soluções:
# Erro: "prepared statement exists"
# Causa: pool_mode=transaction não suporta prepared statements por nome
# Solução A: usar statement ou session mode
# Solução B: desabilitar prepared statements na aplicação
# Prisma (Node.js) — desabilitar prepared statements:
DATABASE_URL="postgresql://...?pgbouncer=true&connection_limit=1"
# prisma/schema.prisma:
# datasource db { url = env("DATABASE_URL"), directUrl = env("DIRECT_URL") }
# SQLAlchemy (Python) — usar NullPool ou QueuePool com prepared=False:
from sqlalchemy import create_engine
engine = create_engine(url, poolclass=NullPool)
# Erro: "max_client_conn reached"
# Solução: aumentar max_client_conn no pgbouncer.ini
# Monitorar com: SHOW CONFIG; e SHOW POOLS;
# Erro: "server login failed"
# Causa: senha incorreta no userlist.txt
# Verificar: sudo journalctl -u pgbouncer -n 50 --no-pager$ runstack deploy --plan starter
Não quer configurar manualmente?
Não quer configurar manualmente? Implante o VPS em menos de 3 minutos com a Runstack. Infraestrutura da OPEN DATACENTER, com servidores no Brasil.