line / armeria

Your go-to microservice framework for any situation, from the creator of Netty et al. You can build any type of microservice leveraging your favorite technologies, including gRPC, Thrift, Kotlin, Retrofit, Reactive Streams, Spring Boot and Dropwizard.
https://armeria.dev
Apache License 2.0
4.83k stars 922 forks source link

Setting ServiceRequestContext#setRequestTimeout to zero causes a service to throw an IllegalArgumentException #4544

Closed chungonn closed 1 year ago

chungonn commented 2 years ago

Below Scala code is similar to the one in Armeria's long SSE tutorial.

@Get("/sse")
@ProducesEventStream
def sse(ctx: ServiceRequestContext, req: HttpRequest)/*: Publisher[ServerSentEvent]*/ =
    ctx.setRequestTimeout(Duration.ZERO)
    Flux.just("foo", "bar", ServerSentEvent.ofData)
}

When the service receives a request, it emits an IllegalArgumentException. Please find the stacktrace below.

Using the following versions - Armeria version 1.20.3, Java 17, Scala 3.2.0

java.lang.IllegalArgumentException: timeoutNanos: 0 (expected: > 0)
    at com.linecorp.armeria.internal.shaded.guava.base.Preconditions.checkArgument(Preconditions.java:206)
    at com.linecorp.armeria.internal.common.CancellationScheduler.setTimeoutNanosFromNow(CancellationScheduler.java:258)
    at com.linecorp.armeria.internal.common.CancellationScheduler.setTimeoutNanos(CancellationScheduler.java:180)
    at com.linecorp.armeria.internal.server.DefaultServiceRequestContext.setRequestTimeout(DefaultServiceRequestContext.java:315)
    at com.linecorp.armeria.server.ServiceRequestContext.setRequestTimeout(ServiceRequestContext.java:413)
    at experiment.armeria.SSEController1$.sse(SSEServer1.scala:67)
    at com.linecorp.armeria.internal.server.annotation.AnnotatedService.invoke(AnnotatedService.java:347)
    at com.linecorp.armeria.internal.server.annotation.AnnotatedService.lambda$serve0$8(AnnotatedService.java:316)
    at java.base/java.util.concurrent.CompletableFuture.uniApplyNow(CompletableFuture.java:684)
    at java.base/java.util.concurrent.CompletableFuture.uniApplyStage(CompletableFuture.java:662)
    at java.base/java.util.concurrent.CompletableFuture.thenApply(CompletableFuture.java:2168)
    at com.linecorp.armeria.internal.server.annotation.AnnotatedService.serve0(AnnotatedService.java:320)
    at com.linecorp.armeria.internal.server.annotation.AnnotatedService.serve(AnnotatedService.java:246)
    at com.linecorp.armeria.server.HttpServerHandler.handleRequest(HttpServerHandler.java:365)
    at com.linecorp.armeria.server.HttpServerHandler.channelRead(HttpServerHandler.java:258)
    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 com.linecorp.armeria.server.Http1RequestDecoder.channelRead(Http1RequestDecoder.java:227)
    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 com.linecorp.armeria.server.HttpServerUpgradeHandler.channelRead(HttpServerUpgradeHandler.java:228)
    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.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
    at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:336)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:308)
    at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
    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.handlerRemoved(ByteToMessageDecoder.java:256)
    at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:526)
    at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:458)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:280)
    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.logging.LoggingHandler.channelRead(LoggingHandler.java:280)
    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.flush.FlushConsolidationHandler.channelRead(FlushConsolidationHandler.java:152)
    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:800)
    at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:499)
    at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:397)
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
    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:833)
ikhoon commented 1 year ago

To disable a timeout to the request, you should use [ServiceRequestContext.clearRequestTimeout()](https://javadoc.io/static/com.linecorp.armeria/armeria-javadoc/1.20.3/com/linecorp/armeria/server/ServiceRequestContext.html#clearRequestTimeout()) instead.

chungonn commented 1 year ago

Hi Ikhoon, thanks once again. Is there any annotation for setting the request to No Time out?

ikhoon commented 1 year ago

We are working on https://github.com/line/armeria/issues/4463 which can control a request timeout using an annotation.

ikhoon commented 1 year ago

Is there any annotation for setting the request to No Time out?

This is a nice idea. I commented about that on https://github.com/line/armeria/pull/4499/files#r1035533526