Incidentes reais são excelentes testes de maturidade técnica. Eles revelam não apenas falhas de código, mas principalmente falhas de premissas arquiteturais.
Este artigo descreve a investigação de um incidente envolvendo perda de conexão com PostgreSQL em ambiente produtivo, durante um pico de eventos. O objetivo aqui não é apontar culpados, mas demonstrar capacidade analítica, organização de evidências e raciocínio estruturado de troubleshooting.
Contexto do Incidente
Durante um pico elevado de processamento de eventos, o backend perdeu conexão com o banco de dados e não conseguiu se recuperar automaticamente. O banco permaneceu saudável durante todo o período. A aplicação, porém, entrou em modo degradado e passou a retornar dados mockados ao invés de consultar o banco real.
Primeira Hipótese: Banco Instável?
Sempre que há perda de conexão, a suspeita inicial costuma ser:
- Failover do RDS
- Reinício automático
- Exaustão de conexões
- Saturação de CPU ou I/O
A investigação começou pelo lado do banco.
Evidências coletadas
- Status do RDS permaneceu Available
- Nenhum evento de failover
- Nenhum reboot automático
- Métricas de CPU e memória estáveis
- Log do banco registrou:
could not receive data from client: Connection reset by peer
Esse log é crucial. Ele indica que quem encerrou a conexão foi o cliente, não o banco. A hipótese de indisponibilidade do RDS foi descartada.
Mudança de Foco: Aplicação
Com o banco saudável, a análise passou para a aplicação.
Observações importantes:
- O container continuou rodando (não houve restart)
- Não houve crash
- A API permaneceu respondendo
- Porém, estava desconectada do banco
Isso é um estado perigoso: sistema aparentemente saudável, mas funcionalmente degradado.
Análise do Código
Ao revisar a implementação do serviço de banco de dados, foi identificado o seguinte padrão:
- Existe uma variável interna
isConnected - Após a primeira conexão bem-sucedida, essa variável é marcada como
true - O sistema passa a assumir que o banco continuará disponível indefinidamente
Problemas estruturais identificados:
-
isConnectedé tratada como fonte de verdade permanente - Não há mecanismo de recriação do pool
- Não existe estratégia de reconexão automática
- Falhas de socket não invalidam o estado interno
- O erro não força reinicialização do container
Durante o pico de eventos, o backend encerrou abruptamente o socket TCP.
Após isso:
- O pool ficou inválido
-
isConnectedpermaneceutrue - A aplicação acreditava estar conectada
- Nenhuma nova tentativa de conexão foi realizada
Resultado: sistema ativo, porém inutilizável do ponto de vista de dados reais.
Causa Raiz
A causa raiz não foi indisponibilidade do banco.
Foi uma falha de resiliência na camada de aplicação.
O sistema foi projetado com a premissa implícita de que:
"Se conectou uma vez, permanecerá conectado."
Essa premissa não é válida em ambientes distribuídos.
Conexões TCP podem ser encerradas por:
- Picos de carga
- Timeouts
- Saturação temporária
- Interrupções de rede
- Problemas internos do runtime
Sem mecanismo de reconexão, qualquer falha transitória se torna permanente até intervenção manual.
Ação Corretiva Imediata
A solução operacional foi simples:
- Reset da task do backend
- Pool recriado
- Conexão restabelecida
- Serviço normalizado
Isso confirmou o diagnóstico: o problema estava no estado interno da aplicação.
Lições Arquiteturais
Este incidente reforça princípios fundamentais de sistemas distribuídos:
- Conexões não são permanentes.
- Pools não são imutáveis.
- Estado interno pode mentir.
- Healthcheck deve validar dependências reais.
- Resiliência não é opcional em cenários de alta carga.
O banco estava saudável. A infraestrutura estava saudável. O problema estava na suposição arquitetural.
Conclusão
O incidente demonstrou que o maior risco não estava na infraestrutura, mas na camada de aplicação. A reinicialização resolveu o estado atual, mas não corrige o problema estrutural. Sistemas robustos não apenas se conectam — eles sabem se reconectar.
E isso faz toda a diferença quando a carga aumenta e o comportamento real do sistema começa a divergir das premissas iniciais de projeto.
Foi uma lição aprendida, não atuei na camada da aplicação, mas no throubleshooting e resolução pontual do incidente, o que estava na minha alçada.
Top comments (0)