Checar HEAD/ETag antes do GET no extrator do ComprasGov - #88
Open
maia-andre wants to merge 5 commits into
Open
Conversation
Cliente S3 criado uma vez por execução (criar_cliente) em vez de um boto3.resource e uma leitura do segredo a cada arquivo — são centenas de checagens por varredura. FalhaHead no lugar da sentinela False em obter_cabecalhos: qualquer falha do HEAD cai no GET de sempre, agora de forma explícita. HEAD 404 numa entrada conhecida documentado: o arquivo sumiu da fonte, mesmo veredito do GET 404. COLIBRI_VERIFICACAO_COMPLETA=1 desliga o atalho e força GET + hash em tudo (modo paranoico). Docstring do módulo descreve a checagem barata.
12 casos com sessão HTTP e bucket simulados: primeira carga, manifesto sem ETag, ETag igual/diferente, objeto sumido do bucket, HEAD com falha de rede, HEAD 503, HEAD 404, ETag renovado sem mudança de conteúdo, modo de verificação completa e compatibilidade com o manifesto anterior (sem as colunas etag/last_modified). tests/__init__.py vazio, como no #69.
registrar_entrada decodificava o CSV inteiro para str e o embrulhava num StringIO para contar as linhas — o StringIO custa 4x o tamanho do arquivo em memória (buffer UCS-4), além da str e do conteúdo. Para o anual de itens de 2025 (3,86 GB) são ~19 GB extras: a carga não cabe em 15 GB de RAM. Ler com csv.reader sobre TextIOWrapper(BytesIO(conteudo)) custa +0 MB e dá a mesma contagem (medido com 100 MB: +395 MB antes, +0 MB depois). Foi o que permitiu rodar a bancada do HEAD/ETag no escopo completo (22,75 GB) nesta máquina.
Nota técnica (Markdown + PDF) com o experimento que mede a troca do GET incondicional pela checagem HEAD/ETag: censo HEAD do escopo de produção (1.212 arquivos, 22,75 GB por rodada), rodadas de escopo completo (22,75 GB -> 0 bytes na varredura sem mudanças; sabotagem de ETag custa exatamente 1 download) e os achados de bancada, incluindo a memória do extrator e as reescritas em lote dos anuais 2021-2024.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolve #87
O que muda com esse PR
Tudo contido em
ingestion/pncp_comprasgov/extract.py; nada muda nopipeline.pynem no dbt.etagelast_modified. Para cada arquivo com ETag conhecido, umHEADna fonte decide se oGETé necessário: ETag igual e parquet ainda no bucket →ignoradosem baixar. Qualquer dúvida (sem ETag, HEAD falhou, objeto sumiu do bucket, ETag divergente) cai no GET de sempre, com conferência de hash — o atalho nunca substitui a verificação, só a antecipa. HEAD 404 numa entrada conhecida viraindisponivel, o mesmo veredito que o GET 404 sempre deu.COLIBRI_VERIFICACAO_COMPLETA=1ignora os ETags e baixa tudo para conferir hash, como hoje.utils.criar_cliente), em vez de recriarboto3.resourcee reler o segredo a cada um dos milhares deHeadObjectda varredura.registrar_entrada(csv.readersobreTextIOWrapper(BytesIO(...))): oStringIOde hoje custa ~4× o tamanho do arquivo em RAM — ~19 GB extras para o anual de itens de 2025 (3,86 GB). Problema pré-existente, independente do ETag; sem ele a rodada completa não cabe em 15 GB.tests/test_pncp_comprasgov_extract.py, sessão HTTP e bucket falsos, sem rede) e da contagem em streaming.docs/nota-extrator-comprasgov-head-etag.md(+.pdf, mesmo conteúdo) com método, números e evidência. Se preferirem não versionar o PDF, tiro e fica só o.md.Compatibilidade: manifestos antigos carregam normalmente (
restval=""preenche as colunas novas). A primeira rodada após o merge custa o mesmo de hoje — baixa tudo e aprende os ETags pelo caminho atual; da segunda em diante a varredura fica barata.Evidência
Bancada em MinIO local contra a fonte real, escopo de produção inteiro (
2021-12-01→ hoje; 1.212 arquivos existentes + 4.176 períodos inexistentes). O censo é um HEAD por candidato: como omainfaz GET incondicional em todo arquivo existente, a soma dosContent-Lengthé o custo exato de uma rodada dele.main, qualquer rodada)alteracoes.csvvazio — nada para o dbt.alteracoes.csv, manifesto e parquets idênticos entremaine patch.Veredito automático contra o censo: 20/20 checagens OK. Detalhe completo na nota em
docs/.Como testar
Para reproduzir a bancada inteira (recorte em ~30–40 min; escopo completo em ~1h15, ~23 GB de banda) tenho o pacote "reproduzir" — mando na hora.
Checklist antes de mesclar na main
ruff format .(branch rebaseada sobre amainpós-Feat/ci GitHub actions #69;pre-commit run --all-filespassa)pytest— 51 passam (38 smoke + 13 deste PR)