Repository navigation
Feedback Quarkus
Findings from the Quarkus 3.40.1.1 to 4.0.0.Beta1 upgrade, recorded in #1714 and PR #1693 (a redo of #1670).
The full record is docs/dev/QUARKUS-4.md on the quarkus-4-redo branch.
Vert.x 5 findings, with their single-file reproducers, are on Feedback: Vert.x; this page keeps what belongs to Quarkus.
Letters (B, E, ...) and codes (Q-*, V-*) refer to that record and to #1714.
Nothing here has been filed upstream yet.
Each Quarkus item has two follow-ups for Sanne:
-
Verify: how to check it on Quarkus
main, with the smallest reproducer we have. - Discuss with the team: the upstream change we propose, and what it costs.
Every item below was re-checked on Quarkus main at 23e9ff0c4d3 (2026-10-26: Vert.x 5.2.2, Netty 4.2.19.Final, Jackson 3.1.8 and 2.21.8).
Sizes come from a minimal native app with only quarkus-vertx, built with GraalVM 25.0.4 and -H:+BuildReport.
None so far: neither #1714 nor #1693 links an upstream Quarkus, Vert.x or Netty issue.
-
B. Netty's async DNS resolver is always linked into native
quarkus-vertxapps (Q-1, low priority). It costs about 0.27 MB in a minimal app, which most apps never use; the behaviour at run time is correct. Worked around in isx withVertxDnsSubstitutions, which is fine there because isx never enables the async resolver. (#1714)-
Verify: the
-J-Dvertx.disableDnsResolver=truethatVertxCoreProcessorpasses to native-image only reaches the builder JVM. At run timeVertxCoreRecorder.convertToVertxOptionssetsvertx.disableDnsResolver=trueunlessquarkus.vertx.use-async-dns=true, and Vert.x 5 reads it inio.vertx.core.spi.dns.AddressResolverProvider.factory. A native app that printsNameResolver'sprovidergetsDefaultAddressResolverProviderby default, andDnsAddressResolverProviderwith-Dquarkus.vertx.use-async-dns=true: the async resolver works in native today. A substitution offactorythat returnsnew DefaultAddressResolverProvider()removes 117 types and about 0.27 MB (0.19 MB of file size; isx's larger proxy measured 1.2 MB), but thenuse-async-dns=trueis silently ignored. -
Discuss with the team: the size can only be saved with a build-time decision, because
use-async-dnsis a run-time property. That means a native build-time property, or a substitution guarded byonlyWithon build-time config, and a documented limit that the async resolver then cannot be enabled at run time. Probably not worth a new knob for 0.27 MB; worth raising only alongside other native size work (Q-4).
-
Verify: the
-
E. Quarkus 4 silently switches every app to Netty's adaptive allocator, which makes
allocator-max-orderinert; andio.netty.noUnsafe=truein native is undocumented for users (Q-7, Q-8). (#1714) Netty chooses its default buffer allocator from the system propertyio.netty.allocator.type(pooled,unpooledoradaptive, read inByteBufUtil). When the property is not set, Netty's own default applies, and that default changed:pooledin Netty 4.1 (Quarkus 3.x),adaptivein Netty 4.2 (Quarkusmain). Quarkus sets several Netty system properties itself (io.netty.allocator.maxOrder=3,io.netty.leakDetection.level, andio.netty.noUnsafein native), but notio.netty.allocator.type. So moving to Netty 4.2 moved every app from the pooled to the adaptive allocator, only because Netty's default changed. This is also whyquarkus.netty.allocator-max-orderstopped working: it setsio.netty.allocator.maxOrder, which only the pooled allocator reads. On 4.1 that was the default, so the setting worked; on 4.2 the adaptive allocator ignores it, with no warning. A user can set-Dio.netty.allocator.type=pooledthemselves, but few would know they need to.-
Verify: in Netty 4.2.19,
ByteBufUtildefaultsio.netty.allocator.typetoadaptive, and onlyPooledByteBufAllocatorreadsio.netty.allocator.maxOrder. Vert.x 5.2'sVertxByteBufAllocatortakesByteBufAllocator.DEFAULT, and nothing in Quarkus sets the type or uses the pooled allocator. In JVM mode, four 32 MB TLS relays left 68 MB of direct memory with the adaptive allocator against 8 MB pooled; it is off-heap, so heap limits do not cap it. The native measurement is still open on our side (D-3).NettyProcessor.build25Specificsetsio.netty.noUnsafe=trueas a native-image system property; its javadoc explains why (JEP 471, quarkus#39907), but no guide does. -
Discuss with the team: set
io.netty.allocator.typeexplicitly, so the choice is Quarkus's and documented rather than inherited from Netty. Either back topooled, keeping the 3.x behaviour and makingallocator-max-orderwork again, or toadaptiveon purpose, with a migration-guide note and a warning whenallocator-max-orderis set. Either way, theallocator-max-orderjavadoc should say it only applies to the pooled allocator.pooledtrades the adaptive allocator's throughput for predictable memory; the choice needs their own benchmarks. Also documentnoUnsafeand why it is set.
-
Verify: in Netty 4.2.19,
-
F. Vert.x 5 API removals and renames need hand ports. The Quarkus part is recipes (I); the list is on Feedback: Vert.x. (#1714)
-
G. Vert.x 5 behaviour changes that still compile. The Quarkus part is a migration-guide list (J); the findings are on Feedback: Vert.x. (#1714)
-
H. Jackson BOM import order (Q-5). The Jackson 3 BOM is still imported before the Jackson 2 one in
bom/application/pom.xml, sojackson-annotationscomes from Jackson 3's BOM. It is consistent today only becausec651056c3c6set Jackson 2 back to 2.21.8, which matches Jackson 3.1.8's annotations 2.21. The next Jackson 2 bump past 2.21 (Dependabot already did 2.22.2 to 2.22.3) brings back the mismatch we hit: databind 2.22 with annotations 2.21. (#1714)-
Verify: with
com.fasterxml.jackson.core:jackson-databindand the 999-SNAPSHOT BOM,mvn dependency:tree -Dincludes=com.fasterxml.jackson.coreshows databind 2.21.8 and annotations 2.21. -
Discuss with the team: manage
jackson-annotationsexplicitly at the version both Jackson lines accept, or swap the imports, or make the two versions move together.
-
Verify: with
-
I.
quarkus updatehas no Vert.x 5 recipes, and the Jackson recipe breaks acatch(Q-6). (#1714)-
Verify:
quarkus-update-recipes1.15.4 (2026-10-15) has 4.0 recipes for Java 21, Jackson 3, config renames, ArC and dropped relocations. It has none for the Vert.x 5 renames (F), the CDI API groupId (jakarta.enterprise:jakarta.enterprise.cdi-apitojakarta.cdi:jakarta.cdi-api) or thesmallrye-common-vertx-contextrename. The Jackson part delegates to OpenRewrite'sUpgradeJackson_2_3; withrewrite-jackson1.29.0 it turnscatch (IOException e)aroundcreateGeneratorintocatch (JacksonException | IOException e)and leaves the one aroundgen.close(). Both then fail withexception java.io.IOException is never thrown in body of corresponding try statement. It also does not keep Jackson 2 defaults. -
Discuss with the team: 4.0 recipes for the Vert.x 5 renames (F), the CDI API groupId and the
smallrye-common-vertx-contextrename, plus TODO markers for the behaviour changes (G). Recipes cannot make behaviour changes safe, so markers are the honest limit there. Thecatchbug belongs toopenrewrite/rewrite-jackson.
-
Verify:
-
J. Migration guide gaps (Q-9, Q-10). (#1714)
-
Verify: the 4.0 migration guide (
Migration-Guide-4.0on the Quarkus wiki) has a Jackson section with a defaults table, but no Vert.x 4 to 5 or Netty 4.2 section, nothing on Jackson 2 still being managed next to a Jackson 3quarkus-jackson, noSORT_PROPERTIES_ALPHABETICALLYin the defaults table, and neither the CDI API groupId nor thesmallrye-common-vertx-contextrename. Q-10: every native build still printsWarning: Using a deprecated option --enable-url-protocols= from command line. Use reachability metadata instead.(NativeImageBuildStep), and theNativeConfigjavadoc still namesjdk-21builder images. -
Discuss with the team: add those sections, with a pointer to the Vert.x 5 migration guide, and the behaviour-change list from G.
Dropping
--enable-url-protocolsmay need reachability metadata in its place, so it is worth asking what replaces it.
-
Verify: the 4.0 migration guide (
-
Q-4. Native size growth (data, not a bug). The minimal app grows by 2.04 MB from Quarkus 3.40.1 to
main: 1.05 MB of machine code, 0.85 MB of image heap, 604 more types. The largest single part is Jackson 3 core, linked even with no JSON in use: 0.44 MB of machine code, against 0.07 MB for Jackson 2 core on 3.40.1. Then about 0.2 MB of FFM support for Netty 4.2's buffer cleaner, 0.23 MB for the Quarkus runtime and SmallRye Config, and 0.06 MB of Netty resolver (B). Vert.x core itself adds only about 0.04 MB, andio.netty.buffershrinks by 0.15 MB.-
Verify: compare
-H:+BuildReportmachine-code trees for a minimalquarkus-vertxapp on 3.40.1 andmain. -
Discuss with the team: what makes Jackson 3 core reachable in an app that never touches JSON, and whether native size per extension is tracked between releases.
Dropping the unconditional
JacksonCodecreflection registration inVertxCoreProcessordoes not help: it saves 3 types and 272 bytes.
-
Verify: compare
-
Q-11. Reflection-free Jackson outside REST (suggestion). The build-time generated (de)serializers still exist only in
quarkus-rest-jackson(quarkus.rest.jackson.optimization.enable-reflection-free-serializers), for REST endpoint types.- Verify: nothing to reproduce.
-
Discuss with the team: whether the generator could be offered for an application's own types and
ObjectMappers. The cost is a public API to maintain.
-
Vert.x-only items (V-T*, V-W*, V-H*, V-P1) are on Feedback: Vert.x.