Pular para conteúdo

Runbook de incidentes comuns (runtime)

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-10

Primeiros passos para sintomas de produção (não build/deploy — isso está em diagnóstico de builds flaky). Observabilidade: todo ERROR vai ao Sentry/GlitchTip (SentryAppender no logback.xml) — o dashboard é o primeiro lugar a olhar. Erros de API saem em RFC 7807 (application/problem+json); o code/detail do corpo ajudam a identificar a causa.

Push FCM não chega ao app

Ordem de verificação:

  1. A instância está com a empresa certa? Cada instância roda com push.empresa (sulplata | onpetrotrading) e envia nos tópicos bl_liberado_<empresa> e nota_venda_emitida_<empresa>. O app precisa estar inscrito no tópico dessa empresa.
  2. Push está ligado? push.enabled=true e as credenciais Firebase presentes (PUSH_CREDENTIALS_PATH → o secret file montado no container). Com push.enabled=true e a credencial ilegível a instância não sobe — o erro de startup nomeia o path (decisão 0030), então "push mudo com a instância no ar" nunca é credencial faltando. Com push.enabled=false o serviço é no-op silencioso.
  3. Testar sem esperar um evento real: POST /debug/send-bl-liberado (tela /debug, login-gated) dispara um push de teste ao tópico da instância. Se o teste chega e o real não, o problema é no gatilho (o PUT XADM não chegou / origem errada), não no FCM.
  4. Falha de envio? O FcmPushService reenvia erros transitórios do FCM (INTERNAL 500, UNAVAILABLE 503, QUOTA_EXCEEDED) com backoff exponencial + jitter (default 3 tentativas, base 1 s → ~1 s / 2 s / 4 s). Um 500 isolado é reabsorvido no retry e não vira alarme. O Sentry/GlitchTip só recebe a exceção quando: (a) esgotam as tentativas de um erro transitório, ou (b) o erro é permanente (UNREGISTERED, INVALID_ARGUMENT, SENDER_ID_MISMATCH, THIRD_PARTY_AUTH_ERROR) — que falha de imediato, sem retry. Tags no evento: push.topic/push.tipo/push.error_code/push.attempts e os extras push.title/push.body. push.attempts=3 (o default) num evento transitório = outage do FCM, não blip. Ajustável via push.retry.max-attempts / push.retry.base-delay-ms.

Detalhe do payload e do gatilho: push FCM · testar push.

PowerSync: app não recebe atualizações (dessincronizado)

O runbook dedicado cobre reset e operação: Runbook PowerSync. Checklist rápido antes:

  1. O serviço PowerSync está no ar? Ver PowerSync via Docker.
  2. As sync rules estão atualizadas para o schema atual? Ver atualizar sync rules.
  3. O last-change avança? O endpoint /api/v1/powersync/lastchange reflete a última mudança aplicada; se ele avança mas o app não recebe, o problema é do lado PowerSync/cliente.
  4. Caso extremo (cliente com estado corrompido): o reset está no Runbook PowerSync.

Erros 500 / exceções em produção

  1. Sentry/GlitchTip primeiro — todo ERROR é capturado; o stack trace e o contexto estão lá.
  2. A resposta HTTP de erro sai em RFC 7807 (application/problem+json): o code e o detail identificam o tipo (validação, rota inexistente, HttpStatusException).
  3. Ingestão XADM (PUT/DELETE /api/v1/xadm) falhando: o corpo XadmResponse traz o resultado por entidade; um 400 é erro de cliente (JSON malformado / dado inválido) — o agente on-premises deve parar e notificar, não re-tentar.

Heartbeat do integrador-client

O integrador-client manda o heartbeat para POST /api/v1/heartbeat e esta instância o repassa ao central-backend (contrato). Os dois ERRORs abaixo são misconfiguração: não se curam sozinhos e repetem a cada heartbeat (até 1 por hora), agrupados numa issue do GlitchTip.

ERROR heartbeat desligado: <ENV> vazio neste integrador-server (resposta 503 heartbeat_desligado). A env nomeada no log (CENTRAL_API_TOKEN, CLIENTE_ID ou as duas) está vazia no recurso Coolify. Desde a decisão 0029, o CLIENTE_ID vazio só derruba o heartbeat quando o COOLIFY_FQDN também não serve (fora do formato int.<slug>.xadm.biz) — a linha cliente_id do heartbeat: no boot diz qual fonte valeu. Cadastrar e fazer redeploy — checklist em implantação. Se esta instância não deveria ter integrador-client, quem está mandando heartbeat é um jar instalado por engano: desligar lá (heartbeat.intervaloMinutos=0).

ERROR heartbeat recusado pelo central-backend: HTTP <status> [<code>] (resposta 502 heartbeat_recusado). O central devolveu 4xx. Pelo code:

No log Causa Correção
HTTP 422 cliente_desconhecido CLIENTE_ID não existe em clientes no central (é o cliente_id, ex. maxsul — não o CLIENTE) corrigir o CLIENTE_ID
HTTP 401 invalid_token / missing_token CENTRAL_API_TOKEN diferente do cadastrado no central alinhar o valor com os recursos do central
HTTP 400 BAD_REQUEST o client mandou dado que passou aqui e o central recusou (ex. iniciado_em malformado — só o central o valida) ver a versão do client instalada
HTTP 404 sem code CENTRAL_URL aponta para o lugar errado (o proxy responde HTML) corrigir ou apagar o CENTRAL_URL (o default é o central de produção)

WARN heartbeat não repassado: central-backend … (resposta 502 central_indisponivel). Central fora, lento ou rede: transitório, fica em WARN de propósito e não abre issue. O próximo heartbeat tenta de novo. Se durar horas, o próprio central acusa silêncio da instalação depois de 6h úteis — o runbook do lado de lá é o heartbeat do integrador-client no central.

Build/deploy falha (não é runtime)

Ver diagnóstico de builds flaky — o que a suíte instrumenta e como ler a falha do job gate.