basedosdados / basedosdados/pipelines

[bug] br_ms_cnes.leito: o paywall da estabelecimento zera o id_municipio da competência nova

Open
#1,750 0 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.