07 Testar, falhar e corrigir
Notas do instrutor para o módulo 07 — cronograma, roteiro de apresentação, falhas comuns e etapas de redefinição.
Material do facilitador correspondente à lição 07 Testar, falhar e corrigir do participante.
Cronograma
Cerca de 20 minutos. Este é o laboratório de incidentes; proteja tempo suficiente para chegar ao diagnóstico antes do módulo 08 e do encerramento. O laboratório executa a falha 01 (diagnóstico em 5 a 15 min). As falhas 02 (5 a 10 min) e 03 (10 a 20 min, e apenas com o conjunto completo de aproximadamente 30 milhões de linhas) continuam disponíveis como incidentes extras opcionais para uma turma mais ágil. Defina um horário-limite durante o ensaio para que o encerramento não fique espremido.
Roteiro de apresentação
- Este é o resultado de todo o trabalho: use tudo que foi construído até aqui para conduzir um incidente real.
- Descreva o sintoma, não a causa, e deixe os agentes convergirem a partir da telemetria.
- Considere exibir lado a lado no projetor os diagnósticos de vários participantes.
- O laboratório executa a falha 01. Se houver tempo para uma rodada extra, acrescente a falha 02 (e a falha 03 apenas quando o conjunto de dados completo tiver sido carregado previamente); entre uma falha e outra, use abaixo a redefinição compartilhada que guarda as alterações e troca de branch.
Gabarito
Não compartilhe esta seção com os participantes; ela existe apenas neste guia e nunca é
adicionada ao repositório do aplicativo. Cada falha é uma pequena alteração em seu próprio branch, criado a partir de
build-workshop-v1;
a correção consiste em revertê-la. Execute uma falha de cada vez. O incidente do laboratório é a falha 01 (5 a 15 min,
com um elemento surpresa — o back-end não tem culpa). Extras opcionais para uma turma mais ágil: falha 02 (5 a 10 min,
aquecimento, o trace revela tudo) e falha 03 somente quando o conjunto completo de dados estiver disponível
(10 a 20 min, raciocínio mais aprofundado sobre o ClickHouse).
Redefinição compartilhada para todas as falhas (o stash preserva as alterações dos participantes e evita que a troca de branch seja bloqueada):
git stash push --include-untracked -m "module-07-fix"
git switch build-workshop-v1
docker compose --env-file .env.workshop \
-f docker-compose.workshop.yml -f docker-compose.otel.yml up -d --buildFalha 02 — estatísticas de zona retornam 500 (fault/02-zone-stats-500)
Mensagem do commit vista pelos participantes: “backend: align zone stats column names with API params”
(altera somente backend/app/query_builders.py e zone_stats_sql). O código agrupa por
pickup_zone_id, uma coluna que não existe em taxi_trips, em vez de
pickup_location_id.
- Sintoma: o mapa coroplético está vazio (os polígonos são renderizados, mas todas as zonas ficam na
faixa mais clara) e a legenda “Query Nms” não aparece; ocorrem sequências de erros 500 em
GET /api/metrics/zone_stats(o React Query tenta novamente três vezes). Todos os outros cartões funcionam normalmente. - Onde está o sinal: um span
clickhouse.queryno ClickStack comerror=True,error.category="query_failed"edb.statementcontendopickup_zone_id AS zone_id; a exceção registrada é o Code 47UNKNOWN_IDENTIFIERdo ClickHouse. Log ERROR correspondente: “ClickHouse query failed (category=query_failed, elapsed_ms=...): ... | sql=...”. - Caminho do diagnóstico: encontre o span com erro 500 -> leia
db.statement->UNKNOWN_IDENTIFIERempickup_zone_id->DESCRIBE taxi_trips(ou compare os construtores de consultas semelhantes) -> a coluna correta épickup_location_id. - Correção: em
zone_stats_sql, troquepickup_zone_idde volta parapickup_location_id(ou executegit revertno commit da falha); recrie o back-end. - Redefinição: use a redefinição compartilhada acima.
Falha 03 — painel lento (fault/03-slow-dashboard)
Mensagem do commit: “backend: window the trend series by bucket in HAVING” (altera
timeseries_sql). A alteração move o predicado da janela de tempo de WHERE para um HAVING no
alias do bucket, de modo que WHERE passa a ser 1. O ClickHouse deixa de conseguir eliminar dados pela
chave primária (car_type, pickup_datetime) e faz uma varredura completa de taxi_trips com dois
estados quantileTDigest, ultrapassando o max_execution_time de 5 s.
- Sintoma: apenas o cartão de tendência “What's happening now?” falha — ele fica carregando e depois mostra “504 Query timed out...”. Todos os outros cartões continuam rápidos.
- Onde está o sinal: um span
clickhouse.querycomerror.category="timeout",db.elapsed_msem torno de 5000 edb.statementmostrandoWHERE 1 ... GROUP BY ts HAVING ts >= ...; a exceção é o Code 159TIMEOUT_EXCEEDED. O contraste com os spans 200 rápidos dos outros endpoints é a pista decisiva. - Caminho do diagnóstico: um cartão lento entre outros rápidos -> abra o span com timeout -> leia o
SQL -> o filtro de tempo está em
HAVING, eWHEREnão tem intervalo parapickup_datetime-> um antipadrão que impede a eliminação pela chave primária e o pushdown de predicado (as consultas semelhantes colocam a janela emWHERE). - Correção: restaure a janela de tempo em
WHERE(reverta o commit da falha); recrie o back-end. - Redefinição: use a redefinição compartilhada acima.
- Ressalva para o instrutor: a gravidade varia conforme o volume de dados e o tamanho do serviço. Com o conjunto de dados completo
carregado (cerca de 30 milhões de linhas) em um serviço da categoria do workshop (que talvez esteja saindo da inatividade), o
timeout de 5 s ocorre de forma consistente; com a pequena carga de exemplo, a consulta apenas fica mais lenta e não retorna necessariamente o erro 504.
Tanto o
send_receive_timeoutdo cliente quanto omax_execution_timedo servidor são de 5 s, portanto uma disputa entre timeouts do soquete pode ocasionalmente aparecer como um erro 500, em vez do 504 esperado — isso é uma propriedade das configurações de base, não da falha.
Falha 01 — mapa não carrega (fault/01-map-not-loading)
Mensagem do commit: “frontend: serve map geojson from /static asset path” (altera o caminho do fetch
em frontend/src/ui/ZoneMap.tsx). O código busca /static/taxi_zones.geojson, que não
existe. Nuance importante: o fallback de SPA do nginx (try_files ... /index.html) retorna HTTP 200
com o documento HTML, em vez de 404. Assim, r.ok é verdadeiro e r.json() lança um
SyntaxError como Unexpected token '<'.
- Sintoma: o cartão do mapa não funciona — os blocos do mapa-base são renderizados, mas não há polígonos de Nova York nem mapa coroplético, e uma mensagem de erro vermelha aparece no próprio cartão. Todo o restante funciona normalmente.
- Onde está o sinal: não há nada no ClickStack do back-end — o recurso nunca chega ao
FastAPI. O console do navegador mostra o erro de análise JSON que menciona
/static/taxi_zones.geojson; a guia Network mostra que a solicitação retornatext/htmlcom status 200; o log de acesso do nginx mostra o fallback. - Caminho do diagnóstico: os traces do back-end estão limpos -> mude para o console do navegador/a guia Network
-> uma solicitação
.geojsonretorna 200text/html-> reconheça a armadilha do fallback da SPA -> o arquivo, na verdade, está na raiz web, em/taxi_zones.geojson. - Correção: reverta o caminho do fetch (duas linhas) ou execute
git revertno commit da falha; recrie o front-end. - Redefinição: use a redefinição compartilhada acima.
- Ponto didático: nem toda falha aparece nos traces do back-end, e um erro 404 pode se disfarçar de 200 por trás do fallback de uma SPA — portanto, leia o corpo real da resposta e o tipo de conteúdo, não apenas o código de status.
- Observação (SDK do navegador): com o SDK do navegador do HyperDX ativado (sobreposição do módulo 05), o
front-end agora expõe a falha no próprio ClickStack — a falha de análise do
ZoneMapé capturada como um spanconsole.erroremServiceName=nyc-taxi-frontend, associado ao span de recurso do 200text/html. Assim, o agente pode localizar a falha pela telemetria sem sair do ClickStack; o console do navegador/a guia Network é uma alternativa, não o único caminho.
Exemplo de resposta do agente (falha 01, por meio do ClickStack MCP):

Diagnóstico esperado: o agente relaciona a falha à solicitação do geojson que retorna 200 text/html (fallback da SPA), ao erro de JSON.parse resultante capturado em nyc-taxi-frontend, e recomenda disponibilizar o recurso em /static/ ou proteger a chamada .json().
Falhas comuns
- Não há telemetria de base suficiente para que o agente localize o problema; o módulo 05 precisa ter sido executado com antecedência.
- Os participantes pulam para a correção antes de confirmar a causa raiz com base nas evidências.
- O sintoma da falha ainda não está visível porque a pilha acabou de ser iniciada — ou, no caso da falha 03, porque foram carregados poucos dados históricos. Medição do ensaio: com a carga padrão de um único mês (cerca de 3,17 milhões de linhas), a falha causada pela diferença entre WHERE e HAVING não é observável — a varredura completa leva cerca de 300 a 560 ms, praticamente igual à linha de base. Carregue vários meses antes de demonstrar a falha 03, ou use a falha 01 (que pode ser reproduzida imediatamente) e trate a falha 03 como uma ilustração em escala.
- A falha 01 não produz nenhum trace no back-end, e a solicitação incorreta retorna 200
text/htmlpelo fallback da SPA, em vez de 404; os participantes podem procurar nos traces indefinidamente. Oriente-os a abrir o console do navegador/a guia Network, onde aparecem o erro de análise JSON e a respostatext/html. - O participante esquece de usar
--builddepois de fazer checkout de um branch de falha, por isso a imagem antiga continua em execução.
Etapas de redefinição
- A redefinição compartilhada, que guarda as alterações e troca de branch, restaura o aplicativo completo sem descartar a tentativa de correção do participante.
- Injete novamente uma falha fazendo checkout de
fault/01-map-not-loading/fault/02-zone-stats-500/fault/03-slow-dashboarde recriando os contêineres com os arquivos Compose do workshop e do otel. - Os sintomas, sinais e correções de cada falha estão na seção Gabarito acima. Mantenha o gabarito apenas neste guia; ele nunca é adicionado ao repositório do aplicativo.