Use isto quando quiser ensinar uma lição difícil por meio de uma falha real — uma linha do tempo do que aconteceu, a causa raiz desenterrada com os 5 porquês, o raio de impacto e as correções com responsáveis.
Este é um exemplar copiável. Leve a seção de demo abaixo para uma lição construída a partir de assets/lesson-template.html — mantenha os tokens de design e o padrão Simples → Técnico intactos.
Na manhã de 14 de março, nosso app principal parou de salvar qualquer coisa. As pessoas conseguiam abri-lo e clicar por aí, mas toda tentativa de gravar dados falhava. O motivo acabou sendo constrangedoramente simples: o disco do servidor de banco de dados estava completamente cheio. Sem espaço para gravar, o banco de dados recusou todos os novos registros.
Um relatório como este percorre a falha com honestidade para que a mesma coisa nunca nos pegue duas vezes. Ele tem quatro partes que você pode ler de cima a baixo: uma linha do tempo dos eventos, a causa raiz encontrada perguntando "por quê?" cinco vezes, um resumo de impacto e uma checklist de correções com um responsável nomeado para cada uma.
Pense nisso como… uma pia de cozinha que transborda porque o ralo vinha entupindo lentamente havia semanas. A inundação é o incidente; a história real é o entupimento que ninguém estava observando — e a correção é um alarme de ralo, não apenas um esfregão.
A instância primária do Postgres (db-prod-01) atingiu 100% de uso em seu volume de dados. O Postgres entrou em estado somente leitura quando o diretório do write-ahead log (WAL) não conseguiu mais fazer o flush, manifestando-se como PANIC: could not write to file "pg_wal/…": No space left on device. A camada de aplicação traduziu isso em HTTP 500 em todo endpoint de escrita, enquanto as leituras do cache continuavam, e foi por isso que o app "parecia" vivo.
Três fatores se acumularam: (1) um nível de log debug verboso enviado para produção duas semanas antes inflou o volume de WAL e de logs; (2) o VACUUM automático estava desabilitado em uma tabela grande do tipo append-only, então as tuplas mortas nunca liberavam espaço; (3) o único alerta de disco era um painel manual que ninguém tinha aberto às 03:00. Não existia nenhum alarme de limiar abaixo de 100%.
Leia de cima a baixo. Os pontos verde-oliva são rotina, os de cor argila são um sinal de alerta, o vermelho é a falha e o verde é a recuperação. Note como o problema começou semanas antes de alguém perceber.
Log em debug enviado para produção
Um deploy mudou o nível de log do banco para debug "temporariamente" para uma investigação. Nunca foi revertido.
Disco ultrapassa 85%
O volume de dados passa silenciosamente de 85% de uso. Nenhum alerta está configurado nesse limiar, então ninguém é acionado.
sinal perdidoDisco chega a 100% — escritas falham
O Postgres não consegue mais fazer o flush do write-ahead log e cai em somente leitura. Todo salvamento no app começa a retornar erros.
início do incidentePrimeiro relato de cliente
Um usuário envia e-mail ao suporte: "Não consigo salvar meu trabalho." O plantão é acionado 3 minutos depois pelo alarme de taxa de erros, não pelo disco.
detectadoCausa raiz identificada
O plantão executa df -h no host e vê 100% /var/lib/postgresql. A causa agora está clara.
Espaço recuperado em emergência
WAL antigo e logs rotacionados são arquivados fora da máquina e o volume cresce em 40 GB. As escritas voltam em minutos.
mitigadoServiço confirmado saudável
Taxa de erros de volta ao patamar normal, latência de escrita normal, nenhum dado perdido. Incidente encerrado; postmortem agendado.
resolvidoContinue perguntando "mas por que isso aconteceu?" até chegar a algo que você possa de fato corrigir. A primeira resposta ("o disco estava cheio") é um sintoma, não uma causa. A quinta resposta é a que vale a pena corrigir.
Por que o app parou de salvar?
O banco de dados recusou todas as escritas.
Por que o banco de dados recusou as escritas?
Seu disco estava 100% cheio — o Postgres não conseguia mais fazer o flush do write-ahead log.
Por que o disco encheu?
O volume de logs e de WAL vinha crescendo de forma incomumente rápida, e as linhas mortas em uma tabela grande nunca foram liberadas.
Por que o crescimento passou despercebido?
Não havia alerta abaixo de 100%. O uso de disco vivia em um painel que ninguém observa às 3 da manhã.
Causa raiz · por que não existia alerta antecipado
A capacidade de disco nunca foi tratada como um SLO de primeira classe. Uma mudança "temporária" no log debug foi enviada sem ticket de reversão, e ninguém era responsável por alertas proativos de capacidade. O sistema não tinha como nos avisar antes de quebrar.
Os 5 porquês encontram a cadeia principal; incidentes reais têm desvios. Aqui: o autovacuum foi desabilitado em events_raw durante uma migração passada e nunca foi reativado; a janela de retenção de WAL estava alta por causa de uma réplica desde então removida; e o runbook de "disco cheio" estava três anos desatualizado. Nenhum sozinho causou a queda, mas cada um encurtou o pavio.
O engenheiro que enviou o log debug agiu de forma razoável dadas as ferramentas — não havia guarda-corpo, lembrete de reversão nem alarme de capacidade. Um postmortem sem culpados corrige o sistema (adicionar o alarme, adicionar tickets de reversão) em vez da pessoa.
O raio de impacto em números — o que custou e a boa notícia no final.
1h 35min
Indisponibilidade de escrita~4.200
Tentativas de salvar falhas~310
Usuários afetados0
Registros perdidosCada correção tem um responsável e uma prioridade. Marque os itens conforme forem entregues — a barra acompanha o progresso. P1 fecha a porta exatamente para esta falha; P2/P3 reforçam a área ao redor dela.