DEV Community

Cover image for O bug de 16 minutos: quando 'dado faltando' é uma corrida contra o relógio
William Scussel
William Scussel

Posted on

O bug de 16 minutos: quando 'dado faltando' é uma corrida contra o relógio

"Seu sistema está perdendo dados"

Um cliente abriu o chamado, convicto. O relatório de retorno bancário enviado por e-mail toda manhã tinha 423 linhas. O mesmo relatório, gerado sob demanda no portal, tinha 1.351.

928 linhas, sumidas. É o tipo de número que faz você presumir o pior.

No fim, nada foi perdido. O bug tinha 16 minutos de largura.

Passo 1: comparar antes de debugar

Nada de código ainda. Primeiro dei um diff nos dois arquivos.

  • Mesmo dia.
  • Mesmo banco.
  • Mesmo filtro de status.
  • Toda linha do arquivo do e-mail estava contida no arquivo do portal.

Ou seja, o dado não estava errado. Estava ausente. Problema diferente.

Passo 2: matar hipóteses com prova, não com opinião

Três causas plausíveis, cada uma derrubada em produção com queries read-only:

"Está dividido entre bancos / anexos."
Não. Todas as 1.351 linhas pertenciam ao mesmo código de banco.

"O dedup contra o dia anterior é agressivo demais."
O relatório subtrai faturas já vistas no dia anterior:

$diff = array_diff(
    array_column($hoje, 'invoice'),
    array_column($ontem, 'invoice')
);
Enter fullscreen mode Exit fullscreen mode

Repliquei contra os dados de produção. Removeu zero linhas. Não era o culpado.

"Os dois relatórios filtram colunas de data diferentes."
Não. Os dois filtravam a mesma coluna transaction_created_at, para a mesma data.

Passo 3: a mesma query, rodada agora, retorna tudo

Foi aqui que a ficha caiu. Com os filtros exatos que o job do e-mail usa:

SELECT COUNT(DISTINCT invoice)
FROM   bank_returns
WHERE  DATE(occurrence_date) = '2026-06-23'
  AND  bank_code = 33
  AND  credit_date IS NOT NULL;   -- "somente pagos"
-- → 1351
Enter fullscreen mode Exit fullscreen mode

O e-mail tinha enviado 423. O banco de dados agora guarda 1.351. Com filtros idênticos. Nada na query mudou. Algo no tempo mudou.

Passo 4: seguir os timestamps

07:15:45  job do relatório roda, monta e envia o e-mail
07:31:02  sistema importa o arquivo de retorno do banco (~12.700 lançamentos do dia)
Enter fullscreen mode Exit fullscreen mode

O relatório roda às 07:15. O arquivo com a maior parte das baixas confirmadas do dia chega às 07:31. O e-mail é gerado 16 minutos antes de o dado existir no banco.

Separe as faturas por esse corte e o gap bate exato:

-- importadas até 07:15  → o que o e-mail viu   → 423
-- importadas após 07:15 → "faltando"           → 928
-- total                                        → 1351
Enter fullscreen mode Exit fullscreen mode

O portal "viu mais" por um motivo chato: foi consultado às 08:23, depois da importação.

O bug de verdade

Não é lógica. É acoplamento temporal entre uma tarefa agendada e a chegada de um dado externo. O relatório assume que os pagamentos de ontem já estão totalmente carregados às 07:15. O banco discorda.

Correções, da mais barata para a melhor:

  • Rodar o relatório depois da importação matinal.
  • Melhor: disparar o relatório ao fim da importação, em vez de um horário fixo.
  • Defensivo: o relatório validar que a importação esperada rodou antes de reportar.

A lição

"Menos linhas" quase nunca é "perdemos dados". Geralmente é "olhei no momento errado".

Antes de anexar um debugger, faça duas perguntas:

  1. Quando esse dado nasce?
  2. Quando eu o leio?

A maioria dos bugs de "dado faltando" morre aí mesmo.

Top comments (0)