Pular para o conteúdo
← Voltar para o Skalablog

Artigo publicado

Connection pooling: como 1.000 requests usam só 20 conexões

Engenharia de SoftwareVercel

Connection pooling resolve um problema simples: 1.000 requests podem compartilhar 20 conexões com o banco, porque cada conexão é criada uma vez, autenticada e devolvida ao pool para o próximo request. Abrir uma conexão nova por cliente esgota o limite do banco e paga de novo o custo de handshake, TLS e autenticação. Este guia mostra o pool no Node.js, o erro do release esquecido e as saídas em serverless.

O que é uma pool de conexões (e por que não é polling)

Uma pool de conexões é um conjunto fixo de conexões abertas que ficam disponíveis para reutilização. O servidor pega uma conexão livre, executa a query e devolve a conexão ao conjunto. Assim, 1.000 requests passam por 20 conexões sem que cada um abra a sua própria conexão com o banco.

Antes de seguir, vale separar duas palavras que soam parecidas em inglês. Pool é piscina: um grupo de recursos reutilizáveis, no caso as conexões. Polling é sondagem: consultar o mesmo recurso várias vezes para verificar se ele está disponível. A confusão é comum, e misturar os dois conceitos leva a desenhar a solução errada.

Quantas conexões o seu banco aguenta

Todo banco tem um teto de conexões simultâneas. Se cada cliente abre a sua, o número máximo de clientes atendidos é igual ao número máximo de conexões aceitas. Com 50 clientes simultâneos, são 50 conexões ocupadas; quando o limite físico é atingido, as novas tentativas falham.

Imagine um banco que aceita 250 conexões. Você não precisa usar todas: pode reservar apenas 10 para uma parte específica da aplicação e deixar o resto para outros serviços, outros servidores ou outros times. Esse é o ponto que muita equipe ignora, porque o orçamento de conexões é compartilhado entre tudo que aponta para o mesmo banco.

O jeito errado: uma conexão por request

Em um backend Express, Fastify, ou qualquer framework parecido, a rota é executada uma vez por request. Se o código cria um client no começo e encerra esse client no fim, cada usuário paga o custo inteiro de abrir uma conexão nova. O padrão funciona até certo ponto, normalmente até 20 ou 30 usuários simultâneos, e então começa a falhar.

Esse código costuma sobreviver anos em produção sem ninguém notar. Muitas empresas têm rotas assim, com tráfego baixo o suficiente para nunca estourar. O problema aparece no primeiro pico.

Abrir uma conexão é caro porque envolve várias etapas:

  1. Handshake de rede entre servidor e banco, com ida e volta de pacotes.
  2. Autenticação das credenciais nos dois lados, servidor e banco.
  3. Negociação de TLS quando a conexão é criptografada.
  4. Alocação de processos e memória para atender aquela sessão.

Repetir isso a cada request é o que transforma uma operação barata em gargalo.

Como instanciar a pool uma única vez

A correção é criar a pool fora da rota e reutilizá-la. No Node.js, cada arquivo é um singleton: o módulo é carregado uma vez e o objeto criado ali vive enquanto o processo existir. Se a pool nasce no escopo do módulo, a rota pode ser chamada por milhares de usuários sem nunca instanciar uma pool nova.

O mesmo raciocínio vale para outras linguagens, mas você precisa saber como ela trata inicialização de módulos e ciclo de vida de objetos. Em Java, por exemplo, a pool costuma vir de um contêiner gerenciado; em Python, de um objeto no escopo do módulo ou de um framework. O princípio é o mesmo: a pool é criada uma vez e compartilhada.

A biblioteca pg do PostgreSQL expõe esse recurso de forma nativa. Você configura o número máximo de conexões e o tempo de espera, e passa a obter clients do pool em vez de criar conexões soltas.

Se você usa uma ORM, provavelmente já tem esse recurso embutido. ORMs modernas expõem configuração de pool na própria documentação, e a recomendação é consultar a doc da sua ORM antes de montar algo na mão, porque muita gente importa pg sem perceber que já estava usando pg por baixo da ORM.

O vazamento de conexões que trava a produção

A pool começa com 10 conexões. Um request pega uma, dá o release, e a conexão volta. No request em que o release é esquecido, a pool fica com nove. Depois oito, depois sete, seis. Quando chega a zero, os novos requests esperam na fila para sempre e a aplicação para de responder.

O sintoma mais confiável de que isso aconteceu é o número de conexões ativas crescendo sem parar sob carga, em vez de oscilar em torno de um patamar estável.

A causa quase sempre está nos caminhos de erro. Se a linha de release só é executada quando a query dá certo, qualquer exceção pula esse trecho e a conexão nunca volta. A solução é liberar a conexão em um bloco finally, ou no equivalente da sua linguagem, para que o release aconteça mesmo quando houver erro.

O padrão correto é este:

  1. Pegar o client do pool no início da operação.
  2. Executar a query dentro de um bloco protegido.
  3. Devolver o client com client.release() no finally.

Esse tipo de vazamento já foi visto em produção, e ele é fácil de detectar justamente porque a aplicação estoura rápido.

Serverless: por que o pool tradicional não funciona

Em um servidor, a pool vive na memória do processo, compartilhada entre todos os requests. Funções serverless não têm memória compartilhada por padrão. Cada função é autossuficiente: ela nasce quando o request chega e morre quando o request sai. Se 20 requests chegam ao mesmo tempo, sobem 20 funções, cada uma com a sua conexão.

Manter uma pool na aplicação, nesse cenário, tem efeito oposto ao pretendido: cada instância abre conexões próprias e o banco recebe mais conexões do que receberia com um servidor tradicional.

A saída é tirar a pool da aplicação e colocá-la em algo que mantém estado. As duas abordagens mais citadas são:

SoluçãoComo funcionaOnde se aplica
RDS ProxyProxy intermediário que mantém a pool e atende cada funçãoAWS, com Lambda e RDS
PG BouncerProxy na frente do PostgreSQL que faz a pool para a aplicaçãoQualquer ambiente, inclusive serverless
Pool da plataformaRecurso gerenciado oferecido pelo provedor de hospedagemVercel Attach Database Pool da Vercel Functions

O nome do recurso muda de plataforma para plataforma, e é por isso que não existe uma resposta única para serverless. A solução depende de onde você está rodando e de como esse ambiente está configurado. Cada provedor tende a amarrar você ao próprio recurso de pool, então vale ler a documentação antes de escolher.

Sobre o PG Bouncer, um aviso honesto: ele funciona como proxy de pool na frente do PostgreSQL, mas o uso dele não é unânime entre quem já trabalha com serverless há mais tempo. Vale testar antes de colocar em produção.

Checklist: sua pool está correta?

  • A pool é criada uma vez, fora da rota ou do handler.
  • O número máximo de conexões cabe no orçamento total do banco, considerando todos os serviços que apontam para ele.
  • Todo caminho de código, inclusive os de erro, devolve a conexão.
  • O release está em um finally ou equivalente.
  • As conexões ativas ficam estáveis sob carga, sem crescer com o tempo.
  • Em serverless, a pool fica em um proxy com estado ou no recurso da plataforma.

Perguntas frequentes

Connection pooling pode vazar dados entre clientes?

Não. O pool reutiliza a conexão física, mas a sessão lógica, as transações e o contexto de cada request ficam isolados na camada de acesso a dados. Um cliente não enxerga a query de outro só porque os dois passaram pela mesma conexão.

Qual é o tamanho ideal de uma pool de conexões?

Não existe número universal. O teto precisa caber no limite do banco e considerar todos os serviços que apontam para ele. Um valor pequeno, como 10 conexões, costuma bastar para cargas moderadas. Se o banco aceita 250 conexões, reservar 10 para uma parte da aplicação e deixar o resto para os outros serviços é uma decisão consciente, não um desperdício.

O que acontece se a pool ficar cheia?

Os novos requests esperam na fila até uma conexão ser devolvida. Se o release nunca acontece, a espera vira indefinida e a aplicação para de responder.

Preciso de pool em serverless?

A arquitetura muda, mas a necessidade de controlar conexões continua. Em vez de um pool na aplicação, você usa um proxy com estado, como o RDS Proxy, ou o recurso de pool da plataforma de hospedagem, como o Attach Database Pool da Vercel Functions.

O ORM já resolve isso por mim?

Na maioria dos casos, sim. ORMs modernas expõem configuração de pool, mas você ainda precisa definir o teto e garantir que as conexões sejam liberadas no fim de cada operação. A documentação da própria ORM é o melhor ponto de partida.

Por que minha aplicação trava depois de um tempo em produção?

Vazamento de conexões é uma causa comum. Verifique se todo caminho de código, inclusive os de erro, devolve a conexão ao pool. O sintoma típico é a contagem de conexões ativas crescendo continuamente em vez de ficar estável.

Qual é a diferença entre polling e pool?

São coisas distintas apesar do nome parecido. Polling é consultar repetidamente um recurso para verificar disponibilidade, como um batedor checando se o serviço respondeu. Pool é um conjunto de recursos reutilizáveis, no caso conexões de banco.

Como sei que estou usando uma pool corretamente?

O sinal mais confiável é o número de conexões ativas ficar estável sob carga, sem crescer com o tempo. Se a contagem sobe a cada pico e não volta, há vazamento.

O pool também ajuda fora do banco de dados?

Sim. O mesmo raciocínio vale para conexões HTTP persistentes e outros recursos caros de estabelecer, desde que o runtime permita reutilizá-las entre requisições.

Onde continuar estudando

A explicação em vídeo que originou este texto está no canal Augusto Galego, e o material de referência sobre backend no Brasil circula bastante em comunidades como o trabalho de Gustavo Dev Doido e o Crazystack Typescript, além de iniciativas de formação como o Bootcamp do Dev Doido. Parte desse conteúdo fica reunida em crazystack.com.br, e vale acompanhar quem publica com frequência sobre bancos de dados e arquitetura.

Se você grava vídeos explicando pools, ORMs ou aquele vazamento de conexões que derrubou a produção, o conhecimento já está registrado em algum lugar. No Skala Blog você cola a URL do vídeo, a ferramenta transcreve o material e gera um artigo que você revisa antes de publicar.

Source video