micronaut-r2dbc icon indicating copy to clipboard operation
micronaut-r2dbc copied to clipboard

Micronaut Data PostgresSql R2DBC GraalVM native image can't connect to a database

Open javaCoincidence opened this issue 2 years ago • 9 comments

Expected Behavior

Expected everything to work same as the JVM version

Actual Behaviour

The application doesn't connect to PostgresSql when packaged natively into a container(dockerBuildNative).

[reactor-tcp-nio-1] ERROR i.m.http.server.RouteExecutor - Unexpected error occurred: Cannot connect to xxx.xx.xx.xxx/:5432 io.r2dbc.postgresql.PostgresqlConnectionFactory$PostgresConnectionException: Cannot connect to xxx.xx.xx.xxx/:5432 at io.r2dbc.postgresql.PostgresqlConnectionFactory.cannotConnect(PostgresqlConnectionFactory.java:216) at reactor.core.publisher.Mono.lambda$onErrorMap$31(Mono.java:3733) at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onError(FluxOnErrorResume.java:94) at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onError(ReactorSubscriber.java:64) at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:129) at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onNext(ReactorSubscriber.java:57) at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onNext(ReactorSubscriber.java:57) at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onNext(ReactorSubscriber.java:57) at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onNext(ReactorSubscriber.java:57) at reactor.core.publisher.MonoDelayUntil$DelayUntilCoordinator.complete(MonoDelayUntil.java:418) at reactor.core.publisher.MonoDelayUntil$DelayUntilTrigger.onComplete(MonoDelayUntil.java:531) at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onComplete(ReactorSubscriber.java:71) at reactor.core.publisher.FluxHandle$HandleSubscriber.onComplete(FluxHandle.java:220) at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onComplete(ReactorSubscriber.java:71) at reactor.core.publisher.FluxHandle$HandleSubscriber.onComplete(FluxHandle.java:220) at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onComplete(ReactorSubscriber.java:71) at reactor.core.publisher.FluxCreate$BaseSink.complete(FluxCreate.java:460) at reactor.core.publisher.FluxCreate$BufferAsyncSink.drain(FluxCreate.java:805) at reactor.core.publisher.FluxCreate$BufferAsyncSink.complete(FluxCreate.java:753) at reactor.core.publisher.FluxCreate$SerializedFluxSink.drainLoop(FluxCreate.java:247) at reactor.core.publisher.FluxCreate$SerializedFluxSink.drain(FluxCreate.java:213) at reactor.core.publisher.FluxCreate$SerializedFluxSink.complete(FluxCreate.java:204) at io.r2dbc.postgresql.client.ReactorNettyClient$Conversation.complete(ReactorNettyClient.java:619) at io.r2dbc.postgresql.client.ReactorNettyClient$BackendMessageSubscriber.emit(ReactorNettyClient.java:885) at io.r2dbc.postgresql.client.ReactorNettyClient$BackendMessageSubscriber.onNext(ReactorNettyClient.java:761) at io.r2dbc.postgresql.client.ReactorNettyClient$BackendMessageSubscriber.onNext(ReactorNettyClient.java:667) at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onNext(ReactorSubscriber.java:57) at reactor.core.publisher.FluxHandle$HandleSubscriber.onNext(FluxHandle.java:126) at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onNext(ReactorSubscriber.java:57) at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200) at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onNext(ReactorSubscriber.java:57) at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) at io.micronaut.reactive.reactor.instrument.ReactorSubscriber.onNext(ReactorSubscriber.java:57) at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:279) at reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:388) at reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:404) at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:93) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:327) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:299) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:658) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:584) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:995) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.lang.Thread.run(Thread.java:833) at com.oracle.svm.core.thread.PlatformThreads.threadStartRoutine(PlatformThreads.java:704) at com.oracle.svm.core.posix.thread.PosixPlatformThreads.pthreadStartRoutine(PosixPlatformThreads.java:202) Caused by: java.lang.IllegalArgumentException: Class java.time.Instant[] is instantiated reflectively but was never registered.Register the class by adding "unsafeAllocated" for the class in reflect-config.json. at com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets.arrayHubErrorStub(SubstrateAllocationSnippets.java:252) at java.util.Collections$UnmodifiableCollection$1.next(Collections.java:1054) at io.r2dbc.postgresql.codec.CachedCodecLookup.afterCodecAdded(CachedCodecLookup.java:70) at io.r2dbc.postgresql.codec.DefaultCodecs.(DefaultCodecs.java:82) at io.r2dbc.postgresql.codec.DefaultCodecs.(DefaultCodecs.java:66) at io.r2dbc.postgresql.PostgresqlConnectionFactory.lambda$doCreateConnection$13(PostgresqlConnectionFactory.java:157) at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:125)

################

The exact same application connects to a database when packaged into a jar and ran inside a container(dockerBuild).

Steps To Reproduce

Micronaut Data R2DBC PostgresSql and use dockerBuildNative to package the application. Fails on the first request to the application

Environment Information

OS: MacOS Monterey. JDK: 17.0.4 Docker: 4.10.1

Example Application

No response

Version

3.5.3

javaCoincidence avatar Jul 29 '22 05:07 javaCoincidence

I'm able to reproduce the same issue with R2DBC driver, the JDBC works fine with a native image. Do we have any update on this issue?

luisospina-sealed avatar Oct 05 '22 20:10 luisospina-sealed

R2DBC makes use of reflection to instantiate some classes. A workaround to be able to use the native image would be adding a reflect-config.json file in the path src/main/resources/META-INF/native-image/your.group/your.artifact/reflect-config.json with the following content:

[
  {
    "name": "java.time.Instant[]",
    "allPublicMethods": true,
    "allDeclaredConstructors": true
  },
  {
    "name": "java.time.ZonedDateTime[]",
    "allPublicMethods": true,
    "allDeclaredConstructors": true
  },
  {
    "name": "java.net.URL[]",
    "allPublicMethods": true,
    "allDeclaredConstructors": true
  },
  {
    "name": "java.util.Date[]",
    "allPublicMethods": true,
    "allDeclaredConstructors": true
  }
]

Not sure if those are all the reflection instantiation usages by R2DBC, but, with this, you should be able to connect to the DB using R2DBC and native image.

luisospina-sealed avatar Oct 06 '22 17:10 luisospina-sealed

Yes its working if you also add

{ "name": "java.net.URI[]", "allPublicMethods": true, "allDeclaredConstructors": true }

javaCoincidence avatar Oct 09 '22 16:10 javaCoincidence

I'm observing similar symptoms with Micronaut 3.7.3, Gradle plugin 3.6.4. Application fails readiness probe when deployed on K8s cluster using assembled native image. From the logs I see that request to database hangs and never returns. If I use non-native image with the same components the issue is gone. If I disable K8s health checks for R2DBC issue is gone. Mentioned workaround doesn't help.

seprokof avatar Nov 14 '22 20:11 seprokof

I'm observing similar symptoms with Micronaut 3.7.3, Gradle plugin 3.6.4. Application fails readiness probe when deployed on K8s cluster using assembled native image. From the logs I see that request to database hangs and never returns. If I use non-native image with the same components the issue is gone. If I disable K8s health checks for R2DBC issue is gone. Mentioned workaround doesn't help.

@seprokof I had the same issue with the mysql connections. I upgraded to the latest micronaut 3.8.3, gradle plugin: 3.7.0 and I used GraalVM 22.3 (sdkman java identifier: 22.3.r17-grl). And the issue disappeared. May be it will solve your problem too.

DmitryKubahov avatar Feb 01 '23 14:02 DmitryKubahov

isn't this related to (and also fixed by) this issue? https://github.com/pgjdbc/r2dbc-postgresql/issues/549

adamkobor avatar Mar 03 '23 10:03 adamkobor

I'm also getting same error. But I'm not using graalvm. I'm using docker build to create image and when run this image, I get below error

{"@timestamp":"2023-08-01T04:47:04.731Z","@version":"1","message":"Health indicator [r2dbc-connection-factory] reported exception: io.r2dbc.postgresql.PostgresqlConnectionFactory$PostgresConnectionException: Cannot connect to localhost/<unresolved>:2709","logger_name":"io.micronaut.management.health.indicator.HealthResult","thread_name":"reactor-tcp-epoll-5","level":"ERROR","level_value":40000,"stack_trace":"io.r2dbc.postgresql.PostgresqlConnectionFactory$PostgresConnectionException: Cannot connect to localhost/<unresolved>:2709\n\tat io.r2dbc.postgresql.PostgresqlConnectionFactory.cannotConnect(PostgresqlConnectionFactory.java:188)

chetansj27 avatar Aug 01 '23 05:08 chetansj27

@msupic please take a look

graemerocher avatar Aug 01 '23 06:08 graemerocher

I succeeded to reproduce the original issue (submitted by @javaCoincidence) when creating a service using Micronaut CLI 3.5.3. The Micronaut CLI 3.5.3 creates a service with org.postgresql:r2dbc-postgresql:0.9.1.RELEASE which doesn't contain any native image metadata. After I replaced 0.9.1.RELEASE with 1.0.2.RELEASE, the issue has been fixed since 1.0.2.RELEASE contains all necessary metadata.

msupic avatar Aug 01 '23 09:08 msupic