Pular para conteúdo

0011 — Habilitar GraalVM native-image no tradutor

Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-09-11 · Decidido em: 2026-08-20

Contexto

A casa adotou native-image como default dos servers Micronaut elegíveis (decisão 0022 do central): RSS ~3× menor e arranque quase instantâneo. O webstorm-ecom deployava só como imagem JVM (eclipse-temurin, java -jar app.jar). Esta decisão habilita o native no lado-app — o que o repositório precisa para o build native ficar verde e a imagem bootar. Padrão provado: piloto bi-transporte-xls e central-backend (ambos native-verde na casa).

Falha de native é só em runtime (reflection/resource/serde sem metadata AOT) — "compila verde" não prova nada; o boot da imagem native é que prova.

Decisão

Habilitação pura, sem tocar domínio (tradutor / ingestão / mensageria). Quatro peças:

  • Perfil no build.gradle.kts — configure<GraalVMExtension> (o accessor graalvmNative {} não é gerado no Kotlin DSL porque o plugin org.graalvm.buildtools.native vem transitivo do io.micronaut.application): -PnativeQuick → -Ob (loop dev/e2e, build ~40-60% mais rápido); sem a flag (prod) → -Os (imagem menor); sempre --gc=serial (menor footprint; default CE explícito). CE 25 não tem --pgo/--emit build-report/--gc=G1 (Oracle-only).
  • Um reflect-config — io.sentry.logback.SentryAppender, em src/main/resources/META-INF/native-image/br.com.xadm/webstorm-ecom/reflect-config.json (nome como string, não .class — evita o javac arrastar org.jetbrains.annotations.Nullable ausente). O logback.xml declara esse appender; o Joran o instancia por reflexão → sem o registro o boot native quebra no LoggerFactory. É o único metadata manual necessário (igual ao piloto e ao central-backend); AWS SDK v2, Flyway e serde são cobertos por reachability-metadata + features do Micronaut. TODO-casa: mover a hint para xadm-comum-web (dona do SentryInitializer) e propagar — some daqui quando a lib absorver.

    Atualização 2026-09-11 — cumprido. A xadm-comum-web 0.9.1 embarca a hint no próprio jar, com três entradas (SentryAppender e SentryOptions com construtor e allPublicMethods, e Level.valueOf); o arquivo local saiu no bump. A cópia daqui registrava só a classe: no native o logback não achava os setters e ignorava o <options>, e os eventos chegavam ao GlitchTip sem o environment.

  • Build via dockerBuildNative do plugin — que gera o Dockerfile native internamente. Não se escreve Dockerfile native à mão e não se adota o template dockerfile-java-native (opcional): o piloto não usa, e divergir do fluxo provado só adiciona risco. O Dockerfile JVM atual fica intacto (prod segue JVM até o rollout, que é de operação). As tasks native do plugin não são configuration-cache-safe → invocar com --no-configuration-cache. Usar dockerBuildNative, não nativeCompile (o native é Linux; nativeCompile num host Windows/Mac geraria binário da plataforma errada) — pré-requisito: daemon Docker.
  • Liveness = /health plano da lib — nada a implementar. O management health fica desligado (endpoints.health.enabled: false, como hoje) e o /health {status,versao} do HealthController da xadm-comum-web serve de liveness. Registrado como não-decisão para não ser reaberto: reabilitar o management health recriaria a rota /health dupla (controller incondicional da lib + management) → 400 em toda request (Norma 2, constituição 0.31.2).

Verificação (gate lado-app)

  • ./gradlew check verde (a config native não altera o build/testes JVM — regressão zero).
  • dockerBuildNative -PnativeQuick --no-configuration-cache compila a imagem native.
  • Boot smoke (Testcontainers GenericContainer da imagem native + Postgres com o DDL emprestado estoque de db/migration-test): afirma boot + Flyway(webstorm_ecom_*) + GET /health 200. É boot-level, segregado do ./gradlew check (exige a imagem native pré-buildada).

"Native build verde aqui" ≠ "app native funciona". O boot smoke prova o grafo de DI + logback/Sentry + serde no boot — não exercita os caminhos native-arriscados de runtime do tradutor: a query data-jdbc EstoqueRepository.pendentes() (réplica) e o PUT real S3→Garage da ingestão. Essa prova é do e2e native, que vive em outro repo (etc/tests/native/e2e-thoms-webstorm-ecom, derivado do e2e-central-backend). O CI (ci.yml) permanece JVM; o gate native é out-of-band, coerente com o estágio protótipo native da casa.

Notas

  • awssdk real = 2.44.7 (a xadm-comum-storage:0.2.0 eleva o BOM 2.28.16 declarado no build.gradle.kts) — se o e2e achar gap de reflexão no S3, procurar metadata da 2.44.x.

Alternativas descartadas

  • dockerfile-java-native (template da toolchain) — o piloto não usa; adotá-lo diverge do fluxo provado sem ganho.
  • @Liveness explícito + management health ligado — mais código e recria a rota /health dupla (400). O /health da lib já é o liveness.
  • Provar S3/data-jdbc no boot smoke local — exigiria Garage + poke no compose deste repo; a spec manteve o e2e ponta-a-ponta no etc/tests (uma fonte, não duas).

Linhagem de trabalho: .ia/013-native-image-*.