basedosdados / basedosdados/pipelines
[bug] br_ms_cnes.leito: o paywall da estabelecimento zera o id_municipio da competência nova
- Dominant language
- Python
- Stars
- 49
- Forks
- 22
- Avg merge
- 19h 31m
- Merged PRs (30d)
- 165
Description
O flow `br_ms_cnes__leito` falhou ao ingerir a competência 2026-06 (run `competent-sambar`,
pool `basedosdados`, 2026-08-04 20:43–20:46):
```
dbt_utils_not_null_proportion_br_ms_cnes__leito_0_95__id_municipio
Got 1 result, configured to fail if != 0
Exception: dbt test falhou para models/br_ms_cnes/br_ms_cnes__leito.sql (target=dev)
```
O `id_municipio` saiu nulo em **100% das 51.927 linhas** da competência, não em pouco mais
que os 5% tolerados.
**A falha é na etapa de dev.** O traceback aponta `crawler/datasus/flows.py:96` — o primeiro
`run_dbt`, `target="dev"`. O flow encerrou antes do upload em produção, do `dbt --target
prod` e do `sync_table_coverage`. `basedosdados.br_ms_cnes.leito` não foi tocada e a
cobertura da tabela não avançou.
## Causa
Os arquivos `LT*.dbf` do DATASUS não trazem município. O modelo o obtém por `left join`
contra a tabela de **produção** (`br_ms_cnes__leito.sql`, linhas 24–43):
```sql
from `basedosdados.br_ms_cnes.estabelecimento`
```
O `profiles.yml` dá contas de serviço diferentes aos dois targets: `dev` usa
`credentials-dev/dev.json` (projeto `basedosdados-dev`), `prod` usa
`credentials-prod/prod.json`. Como o caminho do join é fixo em produção, **o build de dev lê
uma tabela de produção com a credencial de dev** — que não está nos grupos `bd-pro` nem
`sudo`, e portanto cai no `allusers_filter` da Row Access Policy: só enxerga
`ano/mes <= 2025-12`. Zero linha de 2026-06, `left join` sem par, `id_municipio` nulo em
tudo.
Medido na competência (consultas de uma conta com acesso BD Pro):
| Verificação | Resultado |
|---|---|
| linhas de 2026-06 em `basedosdados-dev.br_ms_cnes.leito` / nulas | 51.927 / 51.927 |
| linhas de 2026-06 em `basedosdados.br_ms_cnes.estabelecimento` | 490.186 |
| linhas do join que casam, rodando o mesmo `on` | 51.927 de 51.927 |
As chaves casam e a competência existe. O que faltou ao build de dev foi permissão de
leitura sobre a janela paga.
## Por que passava antes
Os três modelos que carregam paywall (`estabelecimento`, `leito`, `profissional`) declaram
`pre_hook="DROP ALL ROW ACCESS POLICIES ON {{ this }}"`, e as políticas só voltam quando o
flow chega ao `sync_table_coverage`, no fim. Enquanto o flow da `estabelecimento` não
concluía, a tabela ficava **sem políticas** e o build de dev via a competência inteira.
O run `phi854-einstein` das **20:05** foi o primeiro a concluir depois do destravamento do
poll: materializou 2026-06 e reemitiu as duas políticas (`BDpro filter was included`, `All
users filter was included`), com a janela livre rolando para 2025-12. A `leito` rodou 38
minutos depois e ficou cega.
## Não é pontual
A janela livre é `source_end − 6 meses`, então a competência que está sendo ingerida está
**sempre** dentro da janela BD Pro. O build de dev da `leito` nunca vai ver o mês que ele
próprio está processando — isso se repete a cada competência nova, e vale igualmente para a
`profissional`, o outro modelo que lê a `estabelecimento` de produção.
Fica em aberto se o build de `target=prod` sofre do mesmo: ele roda com a credencial de
produção, e o flow nunca chegou nessa etapa para mostrar.
## Consequência a considerar
O modelo é `materialized="incremental"` e o `dbt run` **passou** antes do teste reprovar: as
51.927 linhas de 2026-06 já estão gravadas em dev com `id_municipio` nulo. O filtro
incremental só acrescenta competências mais novas, então uma nova execução não reescreve
essas linhas — corrigir exige apagar a competência ou `--full-refresh`.
## Referências
- Run `competent-sambar`: `38441661-67a9-497a-b942-a7de7d721903`
- Run da `estabelecimento` que reaplicou as políticas: `phi854-einstein`,
`9e61c6af-e696-4487-892e-252d16b1064b`
- `models/br_ms_cnes/br_ms_cnes__leito.sql`, `models/br_ms_cnes/br_ms_cnes__profissional.sql`,
`models/br_ms_cnes/schema.yml`, `profiles.yml`
- #1720 — a `leito` também está na lista de range de partição curto (assunto separado)
Contributor guide
Research direction
Start with crawler/datasus/flows.py:96, profiles.yml, and the joins in models/br_ms_cnes/br_ms_cnes__leito.sql and br_ms_cnes__profissional.sql. Reproduce the dev-target run for competence 2026-06 and inspect the policies and incremental state described in the issue. Done means the dev build can resolve id_municipio for new competencies, relevant dbt tests pass, and existing null rows are repaired without breaking the production flow.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, sql
- Domain
- data-engineering, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100