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

Connection Pooling no Node.js

Atualizado em: 3 de outubro de 2026

Rack de servidores processando fluxos de dados no Node.js

Connection pooling mantém um conjunto de conexões reutilizáveis para banco de dados, Redis, serviços HTTP ou outros recursos caros. Em vez de abrir e fechar uma conexão para cada operação, a aplicação pega uma conexão do pool, executa o trabalho e devolve-a.

O pool reduz latência e overhead, mas também impõe um limite de concorrência. Quando todas as conexões estão ocupadas, novas operações aguardam. Dimensionamento errado pode esgotar o banco, aumentar filas ou causar timeouts em cascata.

Por que conexões são caras

A criação pode envolver:

  • DNS;
  • handshake TCP;
  • TLS;
  • autenticação;
  • negociação de parâmetros;
  • alocação de memória no servidor;
  • inicialização de sessão.

Reutilização evita repetir esse custo.

Pool com PostgreSQL

import pg from 'pg';

const pool = new pg.Pool({
  connectionString: process.env.DATABASE_URL,
  max: 20,
  min: 2,
  idleTimeoutMillis: 30_000,
  connectionTimeoutMillis: 2_000,
});

const result = await pool.query(
  'SELECT id, name FROM users WHERE id = $1',
  [userId],
);

pool.query() pega e devolve uma conexão automaticamente para uma única query.

Transações

Uma transação precisa usar o mesmo client:

const client = await pool.connect();

try {
  await client.query('BEGIN');
  await client.query('UPDATE accounts SET balance = balance - $1 WHERE id = $2', [amount, fromId]);
  await client.query('UPDATE accounts SET balance = balance + $1 WHERE id = $2', [amount, toId]);
  await client.query('COMMIT');
} catch (error) {
  await client.query('ROLLBACK');
  throw error;
} finally {
  client.release();
}

Não use pool.query() para comandos de uma mesma transação, porque cada chamada pode receber conexão diferente.

Sempre liberar

Esquecer release() causa vazamento do pool. Use finally. Se a conexão ficou em estado inválido, descarte-a conforme a API do driver.

Dimensionamento

O total de conexões não é apenas o valor do pool:

conexões totais = réplicas × processos por réplica × max do pool

Dez pods com pool de 20 podem abrir 200 conexões. Some jobs, ferramentas administrativas e réplicas de leitura.

Limite do banco

O banco precisa reservar conexões para manutenção e operações internas. Não configure a soma dos pools igual ao limite máximo. Use margem e, quando necessário, um proxy como PgBouncer.

Little’s Law

Uma aproximação de concorrência é taxa multiplicada pela duração média. Se a aplicação executa 100 queries por segundo e cada query ocupa conexão por 50 ms, a concorrência média é cerca de cinco. Picos e p99 exigem margem.

Pool grande não resolve query lenta

Mais conexões podem aumentar contenção, locks, cache misses e CPU no banco. Primeiro otimize queries, índices e transações.

Fila de espera

Quando o pool está cheio, chamadas aguardam. Uma fila sem limite pode consumir todo o deadline. Aplique timeout para obter conexão e rejeite cedo quando necessário.

Tempo na fila

Meça separadamente:

  • tempo aguardando conexão;
  • tempo da query;
  • tempo total.

Uma query rápida com fila longa indica pool saturado ou transações demoradas.

Connection timeout

O timeout de criação controla quanto esperar para abrir uma nova conexão. Ele não é necessariamente o timeout da query.

Statement timeout

Configure timeout de query no banco ou driver:

SET statement_timeout = '3s';

Em produção, use configuração por sessão ou transação conforme o driver. Cancelar a Promise sem cancelar a query mantém o banco trabalhando.

Idle timeout

Conexões ociosas podem ser fechadas após um período. Valor baixo demais cria churn; alto demais consome slots. Considere tráfego, autoscaling e timeout de rede.

Conexões mínimas

Manter conexões aquecidas reduz latência após períodos ociosos, mas cada réplica consome recursos mesmo sem tráfego. Em ambientes serverless ou autoscaling agressivo, min zero pode ser melhor.

Health check do pool

Não execute uma query cara em toda liveness. Readiness pode verificar capacidade mínima ou uma consulta simples com cache curto. O uso real também fornece sinal.

Conexões quebradas

Rede, failover e restart do banco invalidam sockets. O pool deve remover conexões com erro e criar novas com backoff. Trate eventos do driver.

pool.on('error', (error) => {
  logger.error({ error }, 'Erro em conexão ociosa do pool');
});

Failover

Após troca do primário, conexões existentes podem apontar ao nó antigo. O pool precisa detectar falha e reconectar. DNS e proxies de banco podem ter TTL e comportamento próprios.

Prepared statements

Prepared statements podem ser associados à conexão. Um pool distribui queries entre sessões. Use nomes e cache de acordo com o driver, evitando criar milhares de statements únicos.

Estado de sessão

Alterações como timezone, search_path e variáveis persistem na conexão. Uma requisição pode devolver ao pool uma sessão modificada e afetar a próxima. Configure estado no checkout ou evite mudanças não restauradas.

Tenant por schema

Mudar schema por conexão exige reset no finally. Uma falha pode vazar contexto entre tenants. Prefira queries qualificadas ou transações com configuração local.

Transações longas

Uma transação mantém conexão ocupada e pode segurar locks. Não faça chamadas HTTP dentro dela. Prepare dados antes, execute a transação de forma curta e publique eventos depois por outbox.

N+1 queries

Um endpoint que executa centenas de queries pode monopolizar o pool. Use joins, batching, DataLoader ou consultas em lote.

Concorrência dentro da requisição

Promise.all com muitas queries não torna tudo mais rápido. Ele pode ocupar todo o pool:

// Evite para milhares de itens
await Promise.all(ids.map((id) => repository.findById(id)));

Use limite de concorrência ou uma query com WHERE id = ANY($1).

Pool por banco

Crie pools separados para destinos diferentes. Dentro do mesmo banco, múltiplos pools por módulo podem multiplicar conexões sem necessidade. Centralize o lifecycle.

Read e write pools

Réplicas de leitura podem usar pool separado. Considere consistência após escrita, lag e fallback para primário.

HTTP connection pooling

O mesmo conceito vale para agentes HTTP e Undici. Limite conexões por origem, consuma corpos e monitore fila.

Redis

Redis frequentemente usa poucas conexões multiplexadas. Criar um pool grande pode ser desnecessário. Consulte a biblioteca e o padrão de comandos bloqueantes ou Pub/Sub, que podem exigir conexões dedicadas.

Serverless

Muitas instâncias curtas podem criar tempestade de conexões. Use proxy gerenciado, limite pool por instância e reutilize o cliente entre invocações quando a plataforma permitir.

Kubernetes

Autoscaling baseado em CPU pode aumentar pods e conexões justamente durante pico. Inclua capacidade do banco no planejamento e limite máximo de réplicas.

Graceful shutdown

await stopAcceptingRequests();
await drainRequests();
await pool.end();

Fechar o pool antes de drenar requisições causa falhas. Aplique timeout máximo.

Observabilidade

Monitore:

  • conexões totais;
  • ativas e ociosas;
  • fila de espera;
  • tempo de checkout;
  • tempo de query;
  • timeouts;
  • erros de conexão;
  • reconexões;
  • transações abertas;
  • queries por requisição.

Alertas

Alerte por saturação sustentada, fila crescente, timeout de checkout e conexões próximas ao limite do banco. Correlacione com latência e locks.

Teste de carga

Teste diferentes tamanhos de pool. Meça throughput, p99, CPU do banco, locks, tempo de fila e conexões. O melhor valor não é necessariamente o maior.

Erros comuns

  • não liberar client;
  • usar pool.query em transação;
  • ignorar número de réplicas;
  • pool maior que capacidade do banco;
  • não limitar fila;
  • fazer HTTP dentro de transação;
  • usar Promise.all ilimitado;
  • não resetar estado de sessão;
  • fechar pool cedo no shutdown;
  • aumentar pool para esconder query lenta.

Fluxo recomendado

Calcule o total global, comece com pool pequeno, meça fila e duração e ajuste com o banco. Libere conexões em finally e mantenha transações curtas. Combine com HTTP Keep-Alive, Undici, Graceful Shutdown e métricas em Prometheus.

Consulte a documentação de pooling do node-postgres e a documentação oficial do driver e banco utilizados no projeto.

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