"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')
);
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
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)
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
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:
- Quando esse dado nasce?
- Quando eu o leio?
A maioria dos bugs de "dado faltando" morre aí mesmo.













