micronaut-projects / micronaut-r2dbc

Integration with R2DBC modules
Apache License 2.0
26 stars 8 forks source link

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

Open javaCoincidence opened 2 years ago

javaCoincidence commented 2 years ago

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

luisospina-sealed commented 1 year ago

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 commented 1 year ago

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.

javaCoincidence commented 1 year ago

Yes its working if you also add

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

seprokof commented 1 year ago

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.

DmitryKubahov commented 1 year ago

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.

adamkobor commented 1 year ago

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

chetansj27 commented 1 year ago

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)

graemerocher commented 1 year ago

@msupic please take a look

msupic commented 1 year ago

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.