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 ExhaustionEntendendo Thread Pool Exhaustion
Toda API Spring Boot trabalha com pools de threads.
Exemplo padrão do Tomcat:
server.tomcat.threads.max=200Isso 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: 10Só 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 reiniciaSintomas clássicos
Você provavelmente já viu isso:
Timeout aleatório
Read timed outHikari timeout
Connection is not available, request timed outThreads travadas
http-nio-8080-exec-XXparadas 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 externaResultado: 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: 1800000Mas 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.pendingPrometheus
management:
endpoints:
web:
exposure:
include: prometheusComo identificar thread starvation
Use thread dump.
jstack PIDProcure:
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
