Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
^\.lintr$
^tools$
^pkgdown-site$
^data/(bairro2APS|locs|sinan)\.rda$
^consumer-evidence$
^inla-evidence$
^data/(bairro2APS|locs|sinan)\.rda$
10 changes: 8 additions & 2 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,14 @@ jobs:
extra-packages: any::devtools
needs: check

- name: Run the complete suite against ephemeral PostgreSQL
run: Rscript -e 'devtools::test(stop_on_failure = TRUE)'
- name: Run the package and pinned consumer suite against PostgreSQL
run: Rscript tools/run-postgres-integration.R consumer-evidence

- uses: actions/upload-artifact@v4
if: always()
with:
name: consumer-compatibility-evidence
path: consumer-evidence/

inla-integration:
runs-on: ubuntu-latest
Expand Down
19 changes: 17 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

## Review follow-up

- Sort each municipality-disease series chronologically before nowcasting, Rt
and alert calculation, and identify missing/excess parameter pairs in errors.
Reject mixed-disease results at the legacy conversion boundary.

- Test deprecation on both sides of the 1.3.0 boundary and align the migration
guide with the approved support policy.

- Add mandatory real-INLA integration, controlled host/container benchmarking,
and explicit propagation of fit failures and terminated municipal workers.
Interrupted parallel work cannot be returned as a complete pipeline result.

- Exercise the pinned consumer's state/municipality recovery and RData/SQL
publication against disposable databases. Retain parameter metadata through
municipal concatenation and preserve numeric precision in SQL exports.

- Compare candidate results against the consumer's pinned e460b3b8 calculation
and history functions with immutable reference snapshots. Preserve the
historical ID format for legacy alert objects; modern result IDs are unchanged.
Expand All @@ -25,8 +40,8 @@
- Began package metadata, dependency, documentation, and CI cleanup.
- Isolated all SQL behind an internal DBI repository with centralized physical
table mappings, bound values, quoted identifiers, and stable domain schemas.
- Removed fallback to a global `con`; database-backed public functions now
require an explicit `datasource` connection.
- Require explicit connections in the modern database API; preserve the legacy
pipeline's `datasource = con` default during the approved transition.
- Replaced row-at-a-time writes with transactional staging-table operations and
explicit `error`, `update`, and `ignore` conflict policies.
- Added integration coverage for cases, individual records, climate,
Expand Down
10 changes: 4 additions & 6 deletions R/alert_functions.R
Original file line number Diff line number Diff line change
Expand Up @@ -396,7 +396,6 @@ pipe_infodengue <- function(cities, cid10="A90", datarelatorio, finalday = NULL,
legacy
}


.legacy_alert_parameters <- function(obj) {
parameters <- attr(obj, "parameters")
if (is.null(parameters)) {
Expand Down Expand Up @@ -795,14 +794,13 @@ tabela_historico_intra <- function(obj, iniSE, lastSE, versao,
driver, DBI::Id(schema = config[[1]], table = config[[2]])
))
column_sql <- as.character(DBI::dbQuoteIdentifier(driver, names(dados)))
row_sql <- apply(dados, 1L, function(row) {
values <- Map(function(value, template) {
row_sql <- vapply(seq_len(nrow(dados)), function(index) {
values <- lapply(dados[index, , drop = FALSE], function(value) {
if (is.na(value)) return(DBI::SQL("NULL"))
if (inherits(template, "Date")) value <- as.Date(value)
DBI::dbQuoteLiteral(driver, value)
}, as.list(row), dados[1, , drop = FALSE])
})
paste0("(", paste(as.character(values), collapse = ", "), ")")
})
}, character(1))
sql <- paste("INSERT INTO", table_sql,
paste0("(", paste(column_sql, collapse = ", "), ") VALUES"),
paste(row_sql, collapse = ", "))
Expand Down
64 changes: 53 additions & 11 deletions docs/refactoring/concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,56 @@ insuficientes para definir limites operacionais. Os defaults permanecem 1/1.
O CSV registra o SHA-256 dos fontes R usados; a carga sintética e o supervisor
estão versionados com esta entrega.

## Validação real do motor

O runner `Rscript tools/run-inla-integration.R /tmp/inla-evidence` exige dois
cenários: equivalência serial/paralela e repetição com INLA real, além da
rejeição de saída parcial quando um worker é encerrado. Nenhum pode ser ignorado.
O benchmark `tools/benchmark-inla.py` mede processos externos, municipais e
threads com orçamento explícito e preserva as diferenças entre orçamentos.
A imagem `tools/inla-validation.Dockerfile` fixa a base e o runtime oficial
INLA 25.12.16 para Linux ARM, incluindo a dependência de amostragem `sn`.
Comparação com a referência histórica e recuperação integrada do consumidor
são acrescentadas nas etapas seguintes da cadeia. Os defaults continuam 1/1.
## Validação com INLA real após o review de 29/10/2026

O runner dedicado executa quatro cenários obrigatórios:

```sh
Rscript tools/run-inla-integration.R /tmp/inla-evidence
```

Ele falha se INLA estiver ausente, se algum cenário for ignorado ou se a chamada
bayesiana apenas devolver os casos brutos. Os cenários cobrem repetição,
concorrência municipal com uma e duas threads de ajuste, referência histórica,
fallback real do consumidor com falhas injetadas e encerramento de um worker.
Falha de ajuste preserva seu diagnóstico; a camada paralela rejeita saída parcial.
O caso de encerramento mata um worker controlado, não força OOM no computador.

As entradas são sintéticas: 60 semanas, atrasos de zero a quatro semanas,
dois municípios e dengue/chikungunya. O comparador exige tolerância absoluta
`1e-7`, igualdade de categorias e de ausência de dados. A referência mantém os
corpos históricos imutáveis e adapta somente as dependências externas para o
mesmo ambiente e a semente explícita de amostragem. A comparação com a referência
usa quatro threads de ajuste, como o código antigo. Não valida a instalação
integral da imagem operacional antiga.

Alterar as threads do ajuste não é pressuposto numericamente idêntico. O
benchmark conserva baselines por orçamento de threads, compara concorrência e
repetições contra esses baselines e exporta separadamente diferenças entre
orçamentos. Mudanças de nível de alerta entre orçamentos falham a execução;
outras diferenças permanecem visíveis para avaliação científica.

```sh
python3 tools/benchmark-inla.py /tmp/inla-host --environment host --max-rss-mib 3072
docker build -f tools/inla-validation.Dockerfile -t alerttools-inla-validation .
```

A matriz mede uma ou duas cópias externas da carga, um ou dois processos
municipais e uma ou duas threads de ajuste, com três repetições. As cópias
representam a disputa de recursos entre estados; não são uma execução nacional.
Cada rodada registra tempo de parede incluindo startup, tempo de cálculo nos
workers, versões, entrada, semente e RSS agregado amostrado a cada 100 ms.
O supervisor encerra os grupos de processos ao exceder tempo/memória ou falhar.
O RSS pode contar páginas compartilhadas várias vezes. Em contêiner, as métricas
de cgroup têm escopo da vida inteira do contêiner e são identificadas dessa forma.

A imagem de teste fixa a base por digest e o INLA R/motor em 25.12.16, build
oficial disponível no repositório testing. Isso identifica um ambiente de teste,
não uma recomendação de versão para produção. Os builds ARM 26.08.07/26.08.22
encontrados apresentaram incompatibilidade de biblioteca/instrução na máquina
local. O instalador do build anterior requer carregar INLA; o launcher ARM
precisa de um alias do nome `inla.mkl` para o executável oficial `inla`.
Nenhum cálculo do motor é modificado. O build testa um ajuste real antes de aceitar a imagem.

Os defaults continuam 1/1. Uma medição sintética e uma imagem de teste não
substituem dimensionamento com a carga operacional e revisão científica.
80 changes: 80 additions & 0 deletions docs/refactoring/consumer-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Recorte integrado do consumidor

Os cenários bayesianos reais de recuperação acrescentados após o review de
29/10/2026 são executados pelo runner `tools/run-inla-integration.R`, descrito
em `concurrency.md`. Eles complementam o recorte DBI/SQL abaixo, que permanece
no modo `none`; não equivalem à execução integral da imagem operacional.

O teste executa expressões sem alterações de `main/main_BR.R` na revisão
`27a03f26a9bf366597ef135e7da8d2f620ea9a8e` do AlertaDengueAnalise. O pin do
AlertTools continua sendo `e460b3b8c15ab28e8755f482d576cf7280803b34`. O manifesto
em `tests/reference/consumer-27a03f26` registra o blob original e os hashes das
funções e dos três blocos reais de exportação SQL selecionados.

`tools/prepare-consumer-reference.R` extrai as expressões do fonte original,
verifica seu blob e confirma igualdade das expressões R. Não executa o startup
do consumidor, sua configuração global, SSH/SCP ou um banco operacional.

## O que é executado

- `run_disease_pipeline()`, `run_disease_pipeline_by_city()`,
`run_city_pipeline()` e `combine_ale_parts()` reais;
- processamento estadual de dois municípios, falha estadual injetada com
recuperação completa, isolamento de um município com falha e falha de todos;
- dengue, chikungunya e Zika com fallback legado;
- `load_state_result()` e `normalize_ale()` reais para verificar o objeto `res`
em `.RData`, sua estrutura e conteúdo, sem exigir identidade binária;
- os três blocos reais de exportação SQL por doença;
- persistência DBI idempotente; no PostgreSQL, replay dos SQLs produzidos pelo
escritor da referência e pelo escritor candidato, comparando todas as colunas.

As entradas são sintéticas, usam as consultas reais da candidata e um banco
descartável. As funções de cálculo, conversão e SQL da referência são as do pin
antigo; sua leitura é adaptada à mesma entrada DBI controlada. Esse adaptador não
valida o SQL de leitura antigo ou a instalação integral da imagem operacional.
O modo é `none`, sem inferência de equivalência bayesiana. A precisão numérica é
comparada com tolerância absoluta `1e-7`; há um teste específico de exportação
com `1e-12` para detectar coerção/formatação acidental de probabilidades.
As tabelas são alinhadas pelas chaves municipais, doença, semana e localidade
antes da comparação: a API nova ordena municípios, enquanto o consumidor antigo
pode preservar a ordem de consulta. Chaves duplicadas ou incompletas falham.

## Correções de compatibilidade encontradas

A recuperação municipal concatena listas com `c()`, perdendo o atributo externo
de parâmetros. Cada objeto `alerta` agora carrega os próprios parâmetros como
atributo, sem alterar seus campos públicos. O conversor recupera esses valores
dos filhos e continua rejeitando dados incompletos ou conflitantes.

A geração SQL preserva os tipos de cada célula ao cotar literais, em vez de
converter uma linha inteira em matriz de caracteres. Isso evita formatação
numérica dependente da coerção. A persistência continua com conexão explícita;
nenhum efeito de escrita foi introduzido no cálculo.

## CI e evidências

O job existente de PostgreSQL usa PostgreSQL 16 efêmero e executa
`tools/run-postgres-integration.R`. O runner exige `ALERTTOOLS_TEST_POSTGRES=true`,
o banco dedicado `alerttools_test` e RPostgres instalado; falha se o cenário
PostgreSQL do consumidor for ignorado. Upload preserva CSVs de comparação,
RData/SQLs de ambas as versões, resultados da suíte, ambiente e manifests,
inclusive em uma falha de verificação.

```sh
ALERTTOOLS_TEST_POSTGRES=true PGDATABASE=alerttools_test \
Rscript tools/run-postgres-integration.R consumer-evidence
```

O helper de integração recria schemas somente no banco dedicado de teste.
Para uma validação local sem PostgreSQL, o teste SQLite exercita os mesmos
contratos, artefatos e persistência, mas não substitui o replay PostgreSQL da CI.

## Limites de aprovação

Isto é um recorte representativo do consumidor com origem verificável, não uma
execução nacional do `main_BR.R`. Permanecem pendentes infraestrutura e dados
operacionais anonimizados, avaliação bayesiana, publicação/implantação externa,
responsáveis e aprovação científica. O registro de consumidores não é marcado
como migrado ou validado em produção por causa destes testes. A troca da
dependência no repositório consumidor continua sendo um PR separado, após
essas evidências e seu plano de rollback.
Loading
Loading