Skip to content

fix(captura): grade do ciclo perdia a janela por segundos, e o filtro… - #37

Merged
net0well merged 1 commit into
mainfrom
fix/grade-do-ciclo-e-filtro-de-data
Jul 29, 2026
Merged

net0well merged 1 commit into
mainfrom
fix/grade-do-ciclo-e-filtro-de-data

Conversation

@net0well

Copy link
Copy Markdown
Owner

… de data derrubava a consulta

Grade do ciclo de trinta para cinco minutos

O vencimento da próxima consulta é gravado como agora + IntervaloSemNovidade, onde agora é o instante em que a execução da empresa começou — sempre alguns segundos depois do tique do ciclo, por causa da espera na fila e da chamada à SEFAZ, que leva perto de nove segundos. Somando exatamente uma hora, o vencimento caía sistematicamente logo após o tique correspondente: o ciclo das 16:30:01 não pegou empresas que venciam às 16:30:07 e 16:30:09, e elas esperaram até as 17:00. O intervalo real oscilava entre uma hora e uma hora e meia.

Antecipar a consulta em alguns segundos para acertar o tique não serve: a SEFAZ exige no mínimo uma hora entre consultas depois de um cStat 137, e IntervaloSemNovidade vale exatamente uma hora por causa disso. Sair antes é o gatilho do 656.

O que muda é a granularidade da grade, não o intervalo. A elegibilidade continua sendo a mesma cláusula, então nenhuma consulta à SEFAZ acontece mais cedo do que antes; a empresa que vence poucos segundos depois de um tique espera cinco minutos em vez de trinta. O custo por tique é uma consulta em índice parcial.

Métrica de ciclo vazio

O atraso máximo só enxerga quem já venceu, então o ciclo vazio media zero — inclusive quando havia empresa vencendo segundos depois do tique. Era o ponto cego que escondia exatamente o problema acima.

espera_ate_a_proxima publica quanto falta para a próxima empresa vencer, lido junto de empresas_elegiveis. Zero elegíveis com espera de poucos segundos é o ciclo perdendo a janela por pouco. A consulta extra acontece só no ciclo vazio, onde é a única informação disponível; o caminho que enfileirou algo continua com uma consulta só.

O medidor reporta ausência de medição, e não zero, quando nada está programado: zero diria que uma empresa está vencendo agora e mandaria investigar um ciclo perdido que não existe.

Filtro de data derrubava a consulta de documentos

<input type="date"> devolve 2026-07-29, sem hora e sem fuso, e o binder do ASP.NET produz DateTime com Kind=Unspecified. O Npgsql recusa isso ao comparar com uma coluna timestamptz, e a falha aparecia no meio da execução da consulta, não na validação:

Cannot write DateTime with Kind=Unspecified to PostgreSQL type
'timestamp with time zone', only UTC is supported

FiltroDocumentos e ObterRelatorioPorEmpresaQuery passam a normalizar o período para UTC no próprio contrato, o que cobre os três endpoints afetados e qualquer chamador futuro sem repetir conversão em cada um.

Quem resolve o dia é o navegador, porque é o único que conhece o fuso de quem está olhando a tela: 2026-07-29 vira o intervalo entre o início e o fim daquele dia no relógio local. São três horas de notas em cada ponta que ficariam de fora se o servidor tratasse a data como dia UTC. O fim do dia é 23:59:59.999, e não a meia-noite seguinte, porque o filtro compara com <=.

O filtro e a URL continuam guardando a data pura — é o que o campo exibe de volta e o que faz o endereço permanecer legível e compartilhável. A conversão acontece só ao montar a requisição.

… de data derrubava a consulta

Grade do ciclo de trinta para cinco minutos

O vencimento da próxima consulta é gravado como `agora + IntervaloSemNovidade`, onde `agora` é
o instante em que a execução da empresa começou — sempre alguns segundos depois do tique do
ciclo, por causa da espera na fila e da chamada à SEFAZ, que leva perto de nove segundos.
Somando exatamente uma hora, o vencimento caía sistematicamente logo após o tique
correspondente: o ciclo das 16:30:01 não pegou empresas que venciam às 16:30:07 e 16:30:09, e
elas esperaram até as 17:00. O intervalo real oscilava entre uma hora e uma hora e meia.

Antecipar a consulta em alguns segundos para acertar o tique não serve: a SEFAZ exige no mínimo
uma hora entre consultas depois de um `cStat 137`, e `IntervaloSemNovidade` vale exatamente uma
hora por causa disso. Sair antes é o gatilho do 656.

O que muda é a granularidade da grade, não o intervalo. A elegibilidade continua sendo a mesma
cláusula, então nenhuma consulta à SEFAZ acontece mais cedo do que antes; a empresa que vence
poucos segundos depois de um tique espera cinco minutos em vez de trinta. O custo por tique é
uma consulta em índice parcial.

Métrica de ciclo vazio

O atraso máximo só enxerga quem já venceu, então o ciclo vazio media zero — inclusive quando
havia empresa vencendo segundos depois do tique. Era o ponto cego que escondia exatamente o
problema acima.

`espera_ate_a_proxima` publica quanto falta para a próxima empresa vencer, lido junto de
`empresas_elegiveis`. Zero elegíveis com espera de poucos segundos é o ciclo perdendo a janela
por pouco. A consulta extra acontece só no ciclo vazio, onde é a única informação disponível; o
caminho que enfileirou algo continua com uma consulta só.

O medidor reporta ausência de medição, e não zero, quando nada está programado: zero diria que
uma empresa está vencendo agora e mandaria investigar um ciclo perdido que não existe.

Filtro de data derrubava a consulta de documentos

`<input type="date">` devolve `2026-07-29`, sem hora e sem fuso, e o binder do ASP.NET produz
`DateTime` com `Kind=Unspecified`. O Npgsql recusa isso ao comparar com uma coluna
`timestamptz`, e a falha aparecia no meio da execução da consulta, não na validação:

    Cannot write DateTime with Kind=Unspecified to PostgreSQL type
    'timestamp with time zone', only UTC is supported

`FiltroDocumentos` e `ObterRelatorioPorEmpresaQuery` passam a normalizar o período para UTC no
próprio contrato, o que cobre os três endpoints afetados e qualquer chamador futuro sem repetir
conversão em cada um.

Quem resolve o dia é o navegador, porque é o único que conhece o fuso de quem está olhando a
tela: `2026-07-29` vira o intervalo entre o início e o fim daquele dia no relógio local. São
três horas de notas em cada ponta que ficariam de fora se o servidor tratasse a data como dia
UTC. O fim do dia é 23:59:59.999, e não a meia-noite seguinte, porque o filtro compara com
`<=`.

O filtro e a URL continuam guardando a data pura — é o que o campo exibe de volta e o que faz o
endereço permanecer legível e compartilhável. A conversão acontece só ao montar a requisição.
@net0well
net0well merged commit 61ac161 into main Jul 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant