Repository navigation
spring-boot-starter: fix configuration binding with Jackson 2.21 - #151
Open
JingkaiTang wants to merge 1 commit into
Open
JingkaiTang wants to merge 1 commit into
JingkaiTang wants to merge 1 commit into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## JDK17/Springboot3 #151 +/- ##
=============================================================
+ Coverage 85.67224% 85.78188% +0.10963%
- Complexity 446 450 +4
=============================================================
Files 558 558
Lines 17330 17330
Branches 1509 1509
=============================================================
+ Hits 14847 14866 +19
+ Misses 2483 2464 -19
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #150
TRpcConfigurationBindResultfails during class initialization withNoSuchFieldError: SNAKE_CASEwhen an application uses Jackson Databind 2.21.4. UsePropertyNamingStrategies.SNAKE_CASEin place of the removedPropertyNamingStrategy.SNAKE_CASEfield, preserving snake_case configuration keys and user-provided extension-map keys.Add two regression tests to the starter module's normal test source tree. Dependency versions are unchanged.
Jackson compatibility
PropertyNamingStrategies.SNAKE_CASEhas been available since Jackson Databind 2.12, as documented in the 2.12 API. The current default Jackson versions declared by both tRPC-Java branches already provide this API:JDK17/Springboot3masterTherefore, this replacement requires no dependency upgrade for either branch's default dependency set; this PR leaves all POM versions unchanged. This is an API-availability statement, not a claim that every Jackson release or the master branch's full application suite has been tested. The actual tested versions are listed below. Consumers overriding Jackson below 2.12 cannot use the new API.
Validation
Tested on macOS/aarch64 with Amazon Corretto 17.0.20.1 and 21.0.5, using the native Maven POM and the starter's 19-node dependency reactor. The two new tests pass on both JDKs (0 failures, errors, or skips). JDK 17 validation includes a clean rebuild.
mvn -B -ntp \ -pl trpc-spring-boot-starters/trpc-spring-boot-starter -am \ -Dtest=TRpcConfigurationBindResultTest \ -Dsurefire.failIfNoSpecifiedTests=false clean testThe two regression methods were also invoked directly in separate JVMs with the Jackson runtime jars substituted. On both JDK 17 and JDK 21, the results are:
NoSuchFieldError: SNAKE_CASEThe original binder was compiled from base commit
6d2a46043757c3053745beaecbae9f7ebd0b9a83as a control. These checks cover configuration conversion without starting a Spring context or RPC listener. The full repository suite and Checkstyle workflow have not run locally and remain for upstream CI.Upstream CI status and Netty investigation (2026-10-19)
The original JDK 21 full-test job remains failed; no test or assertion in this PR has been skipped or weakened. JDK 17 full tests, Checkstyle, Maven Install (which skips tests), and Codecov passed. The failed reactor stopped in
trpc-transport-nettybefore building the starter.Scope and historical control
The base-to-head comparison contains only the binder replacement and its two regression tests. Core/Netty code, all POMs, and workflows are unchanged; the Netty reactor has no dependency on the starter. The preceding PR #149 JDK 21 job passed. Its head
f765b458and this PR's base6d2a4604have the same Git tree6278b9c406f61066a47de12b1e1b3c215103bfd0.What the original failure establishes
At the assertion the client had one active connection; client connect/disconnect counts were 1015/1014 and server counts 1772/1771.
getChannels()filters by the underlying channel's active state, so this is not merely a stale inactive entry. The test reverses JUnit's expected/actual arguments (assertEquals(client.getChannels().size(), 0)), explaining the misleadingexpected 1 / actual 0message. Itsclosed=2500counter counts cancellation/close-callback scheduling, not completed channel closes. The fixed five-second sleep and unobserved close stages cannot establish successful asynchronous cleanup.Independently reproduced pre-existing pool race
A deterministic public-API resource-ownership test runs the real
AbstractClientTransport, with mocked channels and a barrier-controlled lock. Two concurrentgetChannel()calls contend for one lazy pool slot. Aftertransport.close(), one created channel remains open on both base and head, on both JDK 17 and JDK 21.The cause is in
ensureChannelActive: it reads/checks the slot before takingconnLock, then replaces it without rereading/rechecking. Two contenders can both retain the old item, publish distinct new items, and both close only the old item, orphaning the overwritten new connection.Eight-way red/green validation:
6d2a46041b027145The candidate moves slot lookup, availability/connecting checks, replacement, and retirement of the old item under the existing lock. It is applied only in isolated CI runners; PR #151 is unchanged. This demonstrates a real existing pool race, not a JDK21-only defect or a binder regression. It is a plausible explanation of the original CI symptom; the original run has no per-channel trace proving this exact interleaving, so it is not claimed as conclusively attributed.
Original-test repetition and candidate validation
The unchanged original test was run 20 times per source/JDK combination, each Maven invocation starting a fresh test JVM:
6d2a46041b027145All three failures were the original line-113 assertion with one active connection. This directly reproduces the original symptom on JDK17 as well as JDK21. These small, independently scheduled runner samples are not a reliable comparison of failure rates. The exact original stress test did not fail in the baseline sample; baseline reproduction above is the deterministic ownership test, not a claim of a baseline stress-test failure. The core/Netty sources and their build/dependency inputs are identical across base/head, and the starter is outside this targeted reactor.
Minimal original-test command (repeat in fresh Maven invocations):
Candidate validation on the baseline passed on both JDK17 and JDK21: 557 Core tests (including the deterministic regression) + 40 Netty tests, zero failures/errors/skips. This is module-level validation, not a full-repository validation or a performance benchmark. The wider critical section could affect contention and deserves review before a production fix.
Conclusion: a pre-existing, cross-JDK connection-pool race is independently demonstrated; no causal path from this starter-only PR to that race was found. The precise interleaving of the original upstream failure remains untraced. Keep a pool fix separate from this Jackson PR, retain the existing assertions, and do not treat longer sleeps as a fix for an orphaned connection.
The integration returned HTTP 403 when asked to rerun the original upstream job, so its failed check remains unchanged. All diagnostic workflows live on separate branches in the author's fork and do not change this PR's head commit.