Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

PgBouncer no Node.js

Atualizado em: 21 de setembro de 2026

Rack de servidores processando fluxos de dados no Node.js

O PgBouncer no Node.js reduz a quantidade de conexões reais abertas no PostgreSQL. A aplicação mantém várias conexões clientes com o pooler, enquanto PgBouncer reutiliza um conjunto menor de conexões do banco. Isso é especialmente útil em Kubernetes, serverless e ambientes com muitas réplicas da API.

Adicionar PgBouncer não elimina a necessidade de um pool no cliente. Também não torna o banco infinitamente escalável. O dimensionamento precisa considerar réplicas, concorrência, transações, prepared statements, migrations e limites do PostgreSQL.

Neste guia, você aprenderá a configurar PgBouncer, escolher session ou transaction pooling, integrar com o pacote pg, definir tamanhos, usar TLS, observar filas e evitar incompatibilidades.

Por que conexões PostgreSQL são caras?

Cada conexão ao PostgreSQL consome memória e processos ou estruturas do servidor. Se vinte Pods Node.js abrirem vinte conexões cada, o total chega a quatrocentas, mesmo que poucas estejam ativas ao mesmo tempo.

O artigo Pool PostgreSQL no Node.js explica o pool do cliente. PgBouncer adiciona uma camada entre os pools e o banco.

Modos de pooling

A configuração oficial do PgBouncer define três modos:

  • session: uma conexão de servidor fica reservada durante toda a sessão cliente.
  • transaction: a conexão volta ao pool ao terminar a transação.
  • statement: a conexão é liberada após cada query; transações multi-statement não funcionam.

Transaction pooling costuma oferecer melhor multiplexação, mas restringe estado de sessão.

Configuração básica

[databases]
app = host=postgres.internal port=5432 dbname=app

[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 40
reserve_pool_size = 10
reserve_pool_timeout = 5
query_wait_timeout = 10
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
admin_users = pgbouncer_admin
stats_users = pgbouncer_stats
server_tls_sslmode = verify-full
server_tls_ca_file = /etc/ssl/postgres-ca.pem

Não copie números sem medir. O pool total precisa caber no limite do banco e deixar margem para administração, migrations e jobs.

Conectando com Node.js

import pg from 'pg';

export const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
  max: 10,
  idleTimeoutMillis: 30_000,
  connectionTimeoutMillis: 3_000,
  maxLifetimeSeconds: 900,
  application_name: 'orders-api'
});

A URL aponta para PgBouncer na porta 6432:

postgresql://app_user:senha@pgbouncer.internal:6432/app

Por que manter pool no cliente?

O pool Node.js reutiliza sockets até PgBouncer e limita concorrência dentro do processo. Sem ele, cada query criaria uma nova conexão cliente, aumentando handshake, autenticação e file descriptors.

Calculando tamanhos

Uma aproximação:

conexões clientes máximas = Pods × pool.max
conexões servidor = pools PgBouncer × default_pool_size

Se existem vinte Pods com max: 10, PgBouncer pode receber duzentas conexões clientes. Ele pode atendê-las com quarenta conexões reais, desde que as transações sejam curtas e a carga permita.

Transaction pooling

Em transaction mode, uma transação pode usar uma conexão diferente da próxima. Portanto, evite depender de:

  • temporary tables entre transações;
  • SET persistente de sessão;
  • advisory locks de sessão;
  • LISTEN mantido na conexão;
  • prepared statements não compatíveis;
  • cursors de sessão.

Estado local da transação, como SET LOCAL, funciona dentro da transação.

Transações no Node.js

const client = await pool.connect();

try {
  await client.query('BEGIN');
  await client.query(
    'INSERT INTO orders(id, total_cents) VALUES ($1, $2)',
    [orderId, totalCents]
  );
  await client.query(
    'INSERT INTO outbox(id, type, payload) VALUES ($1, $2, $3)',
    [eventId, 'order.created', payload]
  );
  await client.query('COMMIT');
} catch (error) {
  await client.query('ROLLBACK');
  throw error;
} finally {
  client.release();
}

Todos os comandos usam o mesmo client e, portanto, a mesma conexão servidor durante a transação. Veja Transações PostgreSQL no Node.js.

Prepared statements

Prepared statements nomeados tradicionalmente dependem da sessão. Versões atuais do PgBouncer podem rastrear prepared statements do protocolo quando max_prepared_statements é maior que zero:

max_prepared_statements = 200

Isso não cobre todos os usos de SQL PREPARE. Teste a biblioteca e a versão. Consulte Prepared Statements no Node.js.

Erro cached plan

Após migrations que alteram tipos ou colunas, planos preparados podem ficar incompatíveis:

ERROR: cached plan must not change result type

Execute RECONNECT no console do PgBouncer após a migration quando necessário e coordene o rollout.

LISTEN/NOTIFY

LISTEN exige uma sessão persistente. Use uma conexão direta ao PostgreSQL ou um PgBouncer em session mode separado. Veja PostgreSQL LISTEN/NOTIFY no Node.js.

Advisory locks

Locks de sessão não são seguros com transaction pooling. Use advisory locks transacionais ou outra estratégia. Consulte Advisory Locks no Node.js.

Migrations

Ferramentas de migration podem precisar de session mode, conexão direta ou parâmetros específicos. Não execute migrations concorrentes a partir de todos os Pods. Use um Job controlado e lock de migration.

Timeout de espera

query_wait_timeout = 10

Se não existe conexão servidor disponível dentro do prazo, PgBouncer encerra o cliente. Isso é melhor que uma fila infinita, mas a aplicação precisa traduzir o erro e aplicar backpressure.

Timeouts no PostgreSQL

Configure no banco ou por transação:

SET LOCAL statement_timeout = '2s';
SET LOCAL lock_timeout = '500ms';

O timeout da API deve ser maior que o statement timeout e menor que o timeout do proxy.

Reserve pool

O reserve pool libera conexões adicionais para clientes que esperam além de um período. Use com cuidado: ele absorve picos, mas pode ultrapassar a capacidade planejada do banco.

Limites por database e usuário

max_db_connections = 100
max_user_connections = 60

Esses limites impedem um banco ou usuário de consumir todas as conexões. Defina filas e alertas antes de atingir o teto.

Autenticação

Prefira SCRAM-SHA-256 e usuários de privilégio mínimo. auth_user pode consultar credenciais por uma função SECURITY DEFINER, evitando acesso direto amplo a pg_authid.

TLS do cliente para PgBouncer

client_tls_sslmode = require
client_tls_key_file = /etc/tls/tls.key
client_tls_cert_file = /etc/tls/tls.crt

Para mTLS, use verify-full e uma CA de clientes. Consulte mTLS no Node.js.

TLS para PostgreSQL

server_tls_sslmode = verify-full
server_tls_ca_file = /etc/ssl/postgres-ca.pem

require criptografa, mas não valida a identidade do servidor. Prefira verify-full quando possível.

Console administrativo

Conecte ao database virtual pgbouncer:

psql -h pgbouncer.internal -p 6432 -U pgbouncer_stats pgbouncer

Comandos úteis:

SHOW POOLS;
SHOW STATS;
SHOW CLIENTS;
SHOW SERVERS;
SHOW DATABASES;
SHOW CONFIG;

Métricas importantes

  • clientes ativos e em espera;
  • conexões servidor ativas e ociosas;
  • tempo médio de espera;
  • transações por segundo;
  • bytes enviados e recebidos;
  • erros de login;
  • pool saturation;
  • conexões máximas do PostgreSQL.

Fila crescente

Clientes em espera indicam transações longas, pool pequeno, queries lentas ou banco saturado. Aumentar conexões pode piorar CPU e I/O. Primeiro investigue duração e planos.

Transações longas

Transaction pooling reutiliza a conexão somente após COMMIT ou ROLLBACK. Uma transação aberta durante chamada HTTP externa bloqueia capacidade. Faça I/O externo fora da transação.

Idle in transaction

idle_transaction_timeout = 60

PgBouncer pode encerrar clientes parados em transação, mas também configure idle_in_transaction_session_timeout no PostgreSQL.

DNS e failover

PgBouncer resolve o host do banco e pode invalidar conexões quando o DNS muda. Ajuste TTL, teste failover e use RECONNECT em eventos planejados. Não dependa de DNS com cache indefinido.

Alta disponibilidade do PgBouncer

PgBouncer é stateless em relação aos dados, mas pode ser ponto único de falha. Execute mais de uma instância atrás de Service, load balancer ou sidecars, considerando distribuição de conexões e limites totais.

Kubernetes

Duas estratégias comuns:

  • PgBouncer central por cluster ou database;
  • sidecar por Pod da aplicação.

Central reduz conexões com mais eficiência. Sidecar simplifica endereço, mas multiplica pools e pode consumir mais conexões servidor.

Graceful shutdown

Durante rollout, use PAUSE, SUSPEND ou draining conforme a estratégia. Não mate PgBouncer com transações ativas sem entender o impacto.

Observabilidade da aplicação

Meça separadamente:

  • tempo esperando pool Node.js;
  • tempo esperando PgBouncer;
  • tempo da query PostgreSQL;
  • tempo total da operação.

Sem essa separação, toda lentidão parece ser do banco.

Erros comuns

  • Pool Node.js grande em cada Pod: clientes excedem max_client_conn.
  • Transaction mode com estado de sessão: comportamento muda entre queries.
  • Prepared statements sem teste: planos não existem na conexão atribuída.
  • Transação durante chamada externa: conexão fica ocupada.
  • Aumentar pool para resolver query lenta: banco satura.
  • Sem TLS: credenciais e dados trafegam expostos.
  • Console aberto: comandos administrativos ficam acessíveis.
  • PgBouncer único: o pooler vira ponto de falha.

Conclusão

O PgBouncer no Node.js multiplica conexões clientes sobre um conjunto menor de conexões PostgreSQL. Transaction pooling oferece alta reutilização, desde que a aplicação não dependa de estado persistente de sessão.

Dimensione pools a partir da capacidade do banco, limite esperas e monitore transações longas. Teste prepared statements, migrations, LISTEN e failover. Com TLS, autenticação e alta disponibilidade, PgBouncer protege o PostgreSQL contra explosões de conexões sem esconder gargalos de queries.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita