From b5eedd75fc40b3c1efcff06dc2f2d150fe1f3e7d Mon Sep 17 00:00:00 2001 From: Jonathan Schneider Date: Sun, 10 May 2026 12:44:09 -0400 Subject: [PATCH] Revert Blackbird default; align jackson-module-parameter-names with Jackson 2.21 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Blackbird default registered in #20 was benched against Jackson 2.17.2, where it showed a ~2x deserialize win. moderne-cli (the primary consumer) and most current openrewrite components have moved to Jackson 2.21, where Blackbird no longer wins on deserialize and *regresses* serialize by ~70% on the real-trace JMH bench (vanilla 317±19 µs vs Blackbird 540±31 µs on a 303 KB GetObject payload — non-overlapping CIs). End-to-end this manifests as visibly slower `mod run` in production after 1.0.8 shipped: BatchVisit p50/p99 times rose 4-12x / 4-65x compared to the 1.0.7 baseline on the JS Applications working set. While here, bump jackson-module-parameter-names from 2.17.2 to 2.21.1 to match what consumers force via their version-alignment rules; this keeps the BOM resolution stable and avoids accidental version skew at runtime. The streaming TokenBuffer-based deserialize from #18 stays — that's the deserialize-side win Blackbird was attempting to chase, already shipped. --- build.gradle.kts | 8 +------- .../moderne/jsonrpc/formatter/JsonMessageFormatter.java | 5 ++--- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 802c93f..298a2cf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,13 +15,7 @@ dependencies { implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310") compileOnly("io.micrometer:micrometer-core:latest.release") - implementation("com.fasterxml.jackson.module:jackson-module-parameter-names:2.17.2") - // Blackbird generates LambdaMetafactory-backed property accessors so - // Jackson skips the reflective MethodHandle path. ~1.5-2x on real RPC - // traffic per a JMH bench replaying a captured trace from `mod run` - // org.openrewrite.node.migrate.upgrade-node-24 — measurable on the - // GetObject deserialize path where field counts in nested Maps are high. - implementation("com.fasterxml.jackson.module:jackson-module-blackbird:2.17.2") + implementation("com.fasterxml.jackson.module:jackson-module-parameter-names:2.21.1") testImplementation("org.openrewrite:rewrite-test:latest.release") } diff --git a/src/main/java/io/moderne/jsonrpc/formatter/JsonMessageFormatter.java b/src/main/java/io/moderne/jsonrpc/formatter/JsonMessageFormatter.java index 60ac3fa..1b7c9de 100644 --- a/src/main/java/io/moderne/jsonrpc/formatter/JsonMessageFormatter.java +++ b/src/main/java/io/moderne/jsonrpc/formatter/JsonMessageFormatter.java @@ -25,7 +25,6 @@ import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.util.TokenBuffer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import com.fasterxml.jackson.module.blackbird.BlackbirdModule; import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; import io.moderne.jsonrpc.JsonRpcError; import io.moderne.jsonrpc.JsonRpcMessage; @@ -48,7 +47,7 @@ public JsonMessageFormatter() { // see https://cowtowncoder.medium.com/jackson-2-12-most-wanted-3-5-246624e2d3d0 .constructorDetector(ConstructorDetector.USE_PROPERTIES_BASED) .build() - .registerModules(new ParameterNamesModule(), new JavaTimeModule(), new BlackbirdModule()) + .registerModules(new ParameterNamesModule(), new JavaTimeModule()) .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) .setSerializationInclusion(JsonInclude.Include.NON_NULL)); mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker() @@ -62,7 +61,7 @@ public JsonMessageFormatter(com.fasterxml.jackson.databind.Module... modules) { this(JsonMapper.builder() .constructorDetector(ConstructorDetector.USE_PROPERTIES_BASED) .build() - .registerModules(new ParameterNamesModule(), new JavaTimeModule(), new BlackbirdModule()) + .registerModules(new ParameterNamesModule(), new JavaTimeModule()) .registerModules(modules) .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) .setSerializationInclusion(JsonInclude.Include.NON_NULL));