Aman-Mittal opened a new pull request, #165:
URL: https://github.com/apache/fineract-consumer-facing/pull/165

   Fixes #45
   
   This adds an opt-in GraalVM native build of the BFF. The native build passes 
the full Cucumber suite, starts in about 0.6 s, and idles under 100 MiB. The 
JVM jar stays the default artifact. The PR is based on `main` and does not 
depend on #163 or #164.
   
   ## Commits
   
   **1. `fix: resolve the Fineract base URL and the audit query toggle at 
runtime`**
   
   Two settings were fixed when bean definitions were created. On the JVM that 
happens at startup, but under Spring AOT it happens at build time, so a native 
image silently ignored the deployment's values:
   
   - **Fineract base URL.** The generated `@FeignClient(url = 
"${fineract.client.base-url}")` baked the build machine's URL into all Fineract 
clients. A new `FineractBaseUrlInterceptor` sets every request's target from 
the runtime property. Feign's `HardCodedTarget` leaves an absolute URL alone, 
so the runtime value wins on both JVM and native.
   - **Audit query toggle.** `AuditQueryController` used 
`@ConditionalOnProperty(consumer.audit.query-enabled)`, so it was dropped at 
build time. The endpoint is now always mapped, and `AuditQueryServiceImpl` 
returns 404 `error.msg.consumer.audit.query.disabled` while the toggle is off. 
A side effect is that the scraped OpenAPI spec is now the same in every 
environment.
   
   **2. `feat: build the BFF as a GraalVM native image`**
   
   - **Build.** `./gradlew -Pnative nativeCompile` uses the Native Build Tools 
plugin (0.11.5, aligned with Boot 4.0.6). The plugin is applied only with 
`-Pnative`, so ordinary `bootJar` and `test` runs gain no AOT processing.
   - **`NativeRuntimeHints`.** This registers the reflection that neither 
Spring AOT nor the GraalVM reachability metadata repository provides. Each 
entry has a comment saying why it is needed, and `NativeRuntimeHintsTest` pins 
them. The gaps were found by running the Cucumber suite against the AOT jar 
under the native-image tracing agent:
     - **Tomcat connector introspection.** Without it the binary fails at 
startup.
     - **Liquibase's `liquibase.change` model**, scanned at AOT time. Without 
it the native image applies identical DDL, verified by diffing `pg_dump 
--schema-only`, but computes different changeset checksums. It would then 
refuse a database migrated by the JVM build, and the reverse.
     - **Spring Security's Jackson modules, mixins and deserializers**, the JDK 
collection types stored as `@class` type ids, and the token fields set on 
read-back. Without them the whole open banking authorization flow fails, 
because `JdbcOAuth2AuthorizationService` cannot read stored authorizations back.
     - **Caffeine's generated cache classes** for `OwnedAccountsCache`, and the 
array types Hibernate instantiates.
   - **Monitoring.** The image is built with `--enable-monitoring=jmxserver`, 
because actuator's Micrometer binders use the platform MBean server. This does 
not open a remote JMX port.
   - **Refresh scope.** `spring.cloud.refresh.enabled=false`. Refresh scope is 
unused here and not supported under AOT.
   - **Container.** `Dockerfile.native` packages a host-built binary, because 
compilation needs about 10 GiB, more than Docker Desktop's VM usually has. 
`compose.native.yaml` swaps it in for the `bff` service. Heap is sized from the 
container limit with `-XX:MaximumHeapSizePercent=60`, and the process exits on 
OOM.
   - **CI.** The new `build-native.yml` workflow:
     - compiles the image;
     - brings up the stack with the native BFF;
     - runs the full Cucumber suite;
     - writes binary size, startup and memory to the job summary.
     - The `graalvm/setup-graalvm` SHA is on the ASF allowlist.
   - **Docs.** `docs/consumer/development/native-image.adoc` covers building, 
testing, footprint, the AOT pitfalls (build-time conditions and placeholders), 
and how to find new reflection gaps with the tracing agent.
   
   ## Native-only unit tests
   
   None are added, because Mockito cannot run in a native image 
(`graalvmNative.testSupport = false`). The functional test for the native build 
is the existing Cucumber suite run against the native container. Code coverage 
keeps coming from the JVM run.
   
   ## Footprint
   
   Each run is the full Cucumber suite (77 scenarios) against the compose stack 
on `main` (`apache/fineract:1.12.1`).
   
   | Build | Container limit | Startup | Idle | Peak during suite | Scenarios |
   |---|---|---|---|---|---|
   | JVM jar (`-Xmx768m`) | 2 GB | 9.7 s | 726 MiB | 846 MiB | 77/77 |
   | Native | 512 MB | 0.6 s | 94 MiB | 120 MiB | 77/77 |
   | Native | 256 MB | 0.6 s | 64 MiB | 83 MiB | 77/77 |
   
   The binary is about 220 MiB. The build takes about 2.5 minutes with a peak 
of about 10 GiB on 22 cores; expect 10–15 minutes on a 4-core runner.
   
   ## Testing
   
   - `./gradlew test`: 489 tests, 0 failures. That includes the new 
`FineractBaseUrlInterceptorTest`, `AuditQueryServiceImplTest` and 
`NativeRuntimeHintsTest`.
   - `./gradlew cucumber` against the JVM build: 77/77.
   - `./gradlew cucumber` against the native build: 77/77 at both 512 MB and 
256 MB limits. These runs used a database first migrated by the JVM build, 
which checks that Liquibase checksums are compatible between the two.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to