FasterXML / jackson-databind

General data-binding package for Jackson (2.x): works on streaming API (core) implementation(s)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Issues using `jackson-databind` 2.17.1 with Reactor (wrt `DeserializerCache` and `ReentrantLock`)

wdallastella opened this issue · comments

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

Related to: #4430 (comment)

After upgrading to Spring Boot 3 (jackson 2.17.1) we start having the bellow issue.

I'm using webflux with reactive-feign. Downgrading jackson to 2.17.0 fix the issue.

java.lang.IllegalMonitorStateException: null
	at java.base/java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:175)
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Assembly trace from producer [reactor.core.publisher.MonoLiftFuseable] :
	reactor.core.publisher.Mono.flatMap(Mono.java:3171)
	reactivefeign.publisher.MonoPublisherHttpClient.executeRequest(MonoPublisherHttpClient.java:25)
Error has been observed at the following site(s):
	*____Mono.flatMap ⇢ at reactivefeign.publisher.MonoPublisherHttpClient.executeRequest(MonoPublisherHttpClient.java:25)
	|_ Mono.transform ⇢ at org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreaker.run(ReactiveResilience4JCircuitBreaker.java:90)
	|_   Mono.timeout ⇢ at org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreaker.run(ReactiveResilience4JCircuitBreaker.java:93)
	|_ Mono.doOnError ⇢ at org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreaker.run(ReactiveResilience4JCircuitBreaker.java:97)
Original Stack Trace:
		at java.base/java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:175)
		at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1007)
		at java.base/java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:494)
		at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:282)
		at com.fasterxml.jackson.databind.deser.DeserializerCache.hasValueDeserializerFor(DeserializerCache.java:219)
		at com.fasterxml.jackson.databind.DeserializationContext.hasValueDeserializerFor(DeserializationContext.java:614)
		at com.fasterxml.jackson.databind.ObjectMapper.canDeserialize(ObjectMapper.java:3673)
		at org.springframework.http.codec.json.AbstractJackson2Decoder.canDecode(AbstractJackson2Decoder.java:112)
		at org.springframework.http.codec.DecoderHttpMessageReader.canRead(DecoderHttpMessageReader.java:97)
		at org.springframework.web.reactive.function.BodyExtractors.readWithMessageReaders(BodyExtractors.java:198)
		at org.springframework.web.reactive.function.BodyExtractors.lambda$toMono$2(BodyExtractors.java:84)
		at org.springframework.web.reactive.function.client.DefaultClientResponse.body(DefaultClientResponse.java:135)
		at org.springframework.web.reactive.function.client.DefaultClientResponse.bodyToMono(DefaultClientResponse.java:155)
		at reactivefeign.webclient.client.WebReactiveHttpResponse.body(WebReactiveHttpResponse.java:53)

Tested as well with 2.18.0, in this case we have blockhound claiming:

reactor.blockhound.BlockingOperationError: Blocking call! jdk.internal.misc.Unsafe#park
	at java.base/jdk.internal.misc.Unsafe.park(Unsafe.java)
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Assembly trace from producer [reactor.core.publisher.MonoLiftFuseable] :
	reactor.core.publisher.Mono.flatMap(Mono.java:3171)
	reactivefeign.publisher.MonoPublisherHttpClient.executeRequest(MonoPublisherHttpClient.java:25)

If I ignore (like bellow), it works, but we might have issues if there is really a blocking call when using reactor.

class TestBlockHoundIntegration : BlockHoundIntegration {
    override fun applyTo(builder: BlockHound.Builder) {
        builder.allowBlockingCallsInside("com.fasterxml.jackson.databind.deser.DeserializerCache", "hasValueDeserializerFor")
    }
}

Version Information

2.17.1

Reproduction

Trying to create a sample project ...

Expected behavior

No issues while parsing messages using jackson-databind and reactor.

Additional context

#4430 (comment)

Will resolve by reverting #4430 in 2.17 (for 2.17.2) -- to go back to synchronized, instead of ReentrantLock -- will separately tackle use of ReentrantLock in 2.18 (for 2.18.0).

Ok, one more change of plans: will re-do ReentrantLock with better scoping (right before try-catch block) based on findings that explain the original problem, as discussed on #4430.

Properly fixed, we believe, will be in 2.17.2 release (https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.17.2) which will probably take a while.

Given 2.17.2 is keeping ReentrantLock in DeserializerCache (avoids pinning on virtual threads):

BlockHound users should add this class to avoid BlockingOperationError

public class JacksonBlockHoundIntegration implements BlockHoundIntegration {
    @Override
    public void applyTo(BlockHound.Builder builder) {
        builder.allowBlockingCallsInside("com.fasterxml.jackson.databind.deser.DeserializerCache", "_createAndCacheValueDeserializer");
    }
}

Given there's no I/O operations in DeserializerCache, the momentary thread parking can be ignored.

This fix also works for 2.17.1

Project reactor users that don't have BlockHound enabled should not be impacted