Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unpredictable reactive controller behavior. #21120

Closed
kazik666 opened this issue Nov 1, 2021 · 10 comments · Fixed by #21121
Closed

Unpredictable reactive controller behavior. #21120

kazik666 opened this issue Nov 1, 2021 · 10 comments · Fixed by #21121
Labels
kind/bug Something isn't working triage/needs-reproducer We are waiting for a reproducer.
Milestone

Comments

@kazik666
Copy link

kazik666 commented Nov 1, 2021

Describe the bug

I decided to refactor one of my service written in quarkus from blocking io to reactive manner.

I have many mock controller which works perfectly before refactor but now some of them throws errors:

java.lang.NullPointerException: Cannot invoke "org.jboss.resteasy.reactive.server.mapping.RuntimeResource.getResourceClass()" because the return value of "org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext.getTarget()" is null
	at org.jboss.resteasy.reactive.server.core.ExceptionMapping.logBlockingErrorIfRequired(ExceptionMapping.java:103)
	at org.jboss.resteasy.reactive.server.core.ExceptionMapping.mapException(ExceptionMapping.java:95)
	at org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext.mapExceptionIfPresent(ResteasyReactiveRequestContext.java:340)
	at org.jboss.resteasy.reactive.server.handlers.ExceptionHandler.handle(ExceptionHandler.java:15)
	at org.jboss.resteasy.reactive.server.handlers.ExceptionHandler.handle(ExceptionHandler.java:9)
	at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:141)
	at org.jboss.resteasy.reactive.server.handlers.RestInitialHandler.beginProcessing(RestInitialHandler.java:47)
	at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:17)
	at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:7)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1127)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:151)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:133)
	at io.quarkus.vertx.http.runtime.StaticResourcesRecorder.lambda$start$1(StaticResourcesRecorder.java:67)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1127)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:151)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:133)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:351)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:329)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1127)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:151)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:133)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:55)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:37)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$10.handle(VertxHttpRecorder.java:443)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$10.handle(VertxHttpRecorder.java:440)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:151)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:133)
	at io.vertx.core.http.impl.Http1xServerRequestHandler.handle(Http1xServerRequestHandler.java:67)
	at io.vertx.core.http.impl.Http1xServerRequestHandler.handle(Http1xServerRequestHandler.java:30)
	at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:50)
	at io.vertx.core.impl.DuplicatedContext.emit(DuplicatedContext.java:168)
	at io.vertx.core.http.impl.Http1xServerConnection.handleMessage(Http1xServerConnection.java:143)
	at io.vertx.core.net.impl.ConnectionBase.read(ConnectionBase.java:155)
	at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:154)
	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.handler.codec.http.websocketx.extensions.WebSocketServerExtensionHandler.channelRead(WebSocketServerExtensionHandler.java:99)
	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.vertx.core.http.impl.Http1xUpgradeToH2CHandler.channelRead(Http1xUpgradeToH2CHandler.java:116)
	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.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
	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:324)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:311)
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:432)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
	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.vertx.core.http.impl.Http1xOrH2CHandler.end(Http1xOrH2CHandler.java:61)
	at io.vertx.core.http.impl.Http1xOrH2CHandler.channelRead(Http1xOrH2CHandler.java:38)
	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.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
	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.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:795)
	at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:480)
	at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:378)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:986)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:831)

For example this controller doesn't work:

package pl.test.rest.recaptcha;

import io.smallrye.mutiny.Uni;
import pl.test.rest.recaptcha.model.SiteVerifyResponse;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.Date;

@Path("/recaptcha/api/siteverify")
public class MockResource {
    @POST
    @Produces(MediaType.APPLICATION_JSON)
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public Uni<SiteVerifyResponse> siteVerify(@FormParam("response") String response
            , @FormParam("secret") String secret, @FormParam("remoteip") String remoteip) {
        return Uni.createFrom().item(SiteVerifyResponse.builder()
                .success(true)
                .score((float) 1.0)
                .action("ACTION")
                .hostname("localhost")
                .challengeTs(new Date())
                .build());
    }
}

But this work's:

package pl.test.rest.dotpay;

import io.smallrye.mutiny.Uni;
import lombok.val;
import pl.test.rest.dotpay.model.PaymentEntry;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.Collections;
import java.util.List;

@Path("/s2/login/api/purchase_history/")
public class MockResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Uni<List<PaymentEntry>> api(@QueryParam("email") String email) {
        return Uni.createFrom().item(Collections.singletonList(createPaymentEntry()));
    }

    private PaymentEntry createPaymentEntry() {
        val entry = new PaymentEntry();
        entry.setDate("2018-06-09T16:54:15.855823");
        entry.setAmount("8.29");
        entry.setCurrency("PLN");
        entry.setStatus("completed");
        entry.setRequestIp("188.146.162.128");
        entry.setMcc("4112");
        entry.setChannel("73");
        entry.setMerchantId("5d51e2dc7bc76cf9841d82ba404d134646bdbd97");

        return entry;
    }
}

The one thing what I'am done in refactor is change the return type to new one with wrapper Uni.

I found on network similar error:
https://github.com/quarkusio/quarkus/runs/3204982867

Expected behavior

No response

Actual behavior

No response

How to Reproduce?

No response

Output of uname -a or ver

Linux 5.13.0-20-generic #20-Ubuntu SMP Fri Oct 15 14:21:35 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

Output of java -version

16.0.2

GraalVM version (if different from Java)

No response

Quarkus version or git rev

2.4.0.Final

Build tool (ie. output of mvnw --version or gradlew --version)

Apache Maven 3.6.2

Additional information

No response

@kazik666 kazik666 added the kind/bug Something isn't working label Nov 1, 2021
@geoand
Copy link
Contributor

geoand commented Nov 1, 2021

Can you please attach a sample project?

@geoand geoand added triage/needs-reproducer We are waiting for a reproducer. and removed triage/needs-triage labels Nov 1, 2021
geoand added a commit to geoand/quarkus that referenced this issue Nov 1, 2021
geoand added a commit to geoand/quarkus that referenced this issue Nov 1, 2021
geoand added a commit to geoand/quarkus that referenced this issue Nov 1, 2021
geoand added a commit to geoand/quarkus that referenced this issue Nov 1, 2021
@kazik666
Copy link
Author

kazik666 commented Nov 1, 2021

Yes of course i'll create sample project.

@geoand
Copy link
Contributor

geoand commented Nov 1, 2021

Thanks.

In any case, #21121 will remove the NPE

@kazik666
Copy link
Author

kazik666 commented Nov 1, 2021

I created demo project:
https://github.com/kazik666/quarkus-reactive-demo

But in this project I doesn't have error. I try to add rest dependencies a reproduce orginal error.

@kazik666
Copy link
Author

kazik666 commented Nov 1, 2021

@geoand I reproduced this error. It occurred when I'am adding filters.

@kazik666
Copy link
Author

kazik666 commented Nov 1, 2021

I found the problem :) In filter i used blocking IO:
org.jboss.resteasy.reactive.common.core.BlockingNotAllowedException: Attempting a blocking read on io thread

I must re-implement my filters.

@kazik666
Copy link
Author

kazik666 commented Nov 1, 2021

I found on StackOverFlow that kind of filter is impossible to implement:
https://stackoverflow.com/questions/67565473/logging-entity-body-with-quarkus-resteasy-reactive

@geoand This is still true in the newest version of quarkus?

@geoand
Copy link
Contributor

geoand commented Nov 1, 2021

If you are trying to read the entire body of the request on the event loop thread, then yes, that is still not allowed

@kazik666
Copy link
Author

kazik666 commented Nov 1, 2021

Ok I'am closing this issue.

@kazik666 kazik666 closed this as completed Nov 1, 2021
@quarkus-bot quarkus-bot bot added this to the 2.5 - main milestone Nov 1, 2021
stuartwdouglas added a commit that referenced this issue Nov 1, 2021
Better handle RuntimeResource when an exception occurs
@JohnnMartins
Copy link

If you are trying to read the entire body of the request on the event loop thread, then yes, that is still not allowed

Why it is not allowed yet? and is there any chance it is allowed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Something isn't working triage/needs-reproducer We are waiting for a reproducer.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants