O Erro Que Está Destruindo APIs Spring Boot em Produção: Thread Pool Exhaustion + Connection Pool Exhaustion

O Erro Que Está Destruindo APIs Spring Boot em Produção: Thread Pool Exhaustion + Connection Pool Exhaustion

Entenda como Thread Pool Exhaustion e Connection Pool Exhaustion estão derrubando APIs Spring Boot em produção, por que o problema é difícil de identificar e como resolver corretamente utilizando observabilidade, tuning e boas práticas.

nexucodeplay09 de maio de 2026
Compartilhar:LinkedInXWhatsAppFacebook

O problema que parece “aleatório”

A API funciona normalmente.

De repente:

  • começa a ficar lenta

  • aumenta timeout

  • CPU sobe

  • banco começa a sofrer

  • Kubernetes reinicia pod

  • cliente reclama

E o pior: muitas vezes o problema não aparece claramente no log.

O time geralmente acha que é:

  • banco lento

  • problema no Kubernetes

  • falta de CPU

  • GC

  • problema de rede

Mas o verdadeiro problema costuma ser outro.


O que está acontecendo de verdade

Na maioria dos casos:

  • as threads da aplicação ficam bloqueadas

  • as conexões do banco acabam

  • novas requisições começam a esperar

  • o sistema entra em efeito cascata

Isso gera dois problemas ao mesmo tempo:

Thread Pool Exhaustion
+
Connection Pool Exhaustion

Entendendo Thread Pool Exhaustion

Toda API Spring Boot trabalha com pools de threads.

Exemplo padrão do Tomcat:

server.tomcat.threads.max=200

Isso significa: no máximo 200 requisições simultâneas processando

Agora imagine:

  • chamadas lentas

  • integração externa

  • query pesada

  • Thread.sleep()

  • bloqueio I/O

As threads começam a ficar presas.

Quando todas ficam ocupadas: novas requisições entram em fila, timeout começa


O problema piora com o banco

Agora entra o segundo problema.

O HikariCP possui limite de conexões:

spring:
  datasource:
    hikari:
      maximum-pool-size: 10

Só 10 conexões.

Se:

  • queries ficam lentas

  • transações demoram

  • thread segura conexão muito tempo

acabou o pool.

Novas requisições:

  • ficam esperando conexão

  • seguram thread

  • congestionam o Tomcat

Aí começa o efeito dominó.


O ciclo da destruição

Fluxo clássico:

requisição lenta
↓
thread ocupada
↓
conexão do banco presa
↓
pool esgota
↓
novas threads aguardam conexão
↓
fila cresce
↓
timeout
↓
pod reinicia

Sintomas clássicos

Você provavelmente já viu isso:

Timeout aleatório

Read timed out

Hikari timeout

Connection is not available, request timed out

Threads travadas

http-nio-8080-exec-XX

paradas por muito tempo.


CPU sobe sem motivo aparente

Porque:

  • thread switching explode

  • fila aumenta

  • retries começam


O erro que quase ninguém percebe

O problema raramente é: “quantidade de usuários”

Na maioria das vezes é:

  • operação bloqueante

  • integração lenta

  • query ruim

  • conexão segurada demais


Exemplo real do problema

Código problemático

@Transactional
public void processOrder() {

    Order order = repository.findById(1L).orElseThrow();

    externalApi.call();

    repository.save(order);
}

O problema:

transação aberta
+
esperando API externa

Resultado: conexão do banco presa durante chamada HTTP

Isso destrói o pool rapidamente.


Como resolver corretamente

Separar transação da chamada externa

public void processOrder() {

    Order order = repository.findById(1L).orElseThrow();

    externalApi.call();

    updateOrder(order);
}

@Transactional
public void updateOrder(Order order) {
    repository.save(order);
}

Agora:

  • conexão fica aberta menos tempo

  • pool respira


10. Ajustando HikariCP

Configuração recomendada

spring:
  datasource:
    hikari:
      minimum-idle: 10
      maximum-pool-size: 30
      idle-timeout: 30000
      connection-timeout: 20000
      max-lifetime: 1800000

Mas atenção: aumentar pool não resolve arquitetura ruim.


Observabilidade obrigatória

Sem observabilidade: você fica cego.


Actuator

<artifactId>spring-boot-starter-actuator</artifactId>

Métricas do Hikari

hikaricp.connections.active
hikaricp.connections.pending

Prometheus

management:
  endpoints:
    web:
      exposure:
        include: prometheus

Como identificar thread starvation

Use thread dump.

jstack PID

Procure:

  • threads WAITING

  • BLOCKED

  • chamadas HTTP lentas


Outro vilão: chamadas bloqueantes

Exemplo:

Thread.sleep(5000);

ou:

restTemplate.getForObject(...)

em excesso.


O problema em Kubernetes

Em Kubernetes isso piora muito.

Porque:

  • readiness falha

  • liveness falha

  • pod reinicia

  • tráfego aumenta nos outros pods

efeito cascata.


Soluções modernas

Virtual Threads (Java 21+)

Excelente para I/O.


WebFlux

Bom para alta concorrência.


Kafka / processamento assíncrono

Evita thread bloqueada.


Cache

Reduz carga no banco.


Checklist de produção

  • evitar transação longa

  • não chamar API dentro de transaction

  • monitorar Hikari

  • monitorar thread pool

  • usar timeout

  • usar retry controlado


O maior erro das equipes

O time geralmente aumenta:

  • CPU

  • memória

  • replicas

Mas não resolve: o gargalo real.


Conclusão

Thread Pool Exhaustion e Connection Pool Exhaustion são silenciosos.

No começo:

  • parece lentidão normal

Depois:

  • derruba produção inteira

A maioria dos problemas não está no Spring Boot.

Está na forma como a aplicação usa:

  • threads

  • transações

  • conexões

Publicidade

💬 Deixe seu comentário

Deixe um comentário

Comentários passam por moderação para evitar spam e manter a qualidade.

Comentários

Ainda não tem comentários.