Open michaelpearce-at opened 3 years ago
Hi @michaelpearce-at,
I think the request context is gone after awaitExchange
is called so we should do the request scoping by ourselves.
Could you try that using the coroutin dispatcher?
https://github.com/line/armeria/blob/master/examples/context-propagation/kotlin/src/main/kotlin/example/armeria/contextpropagation/kotlin/MainService.kt#L43
Hi @minwoox, thanks for the quick response!
I can confirm that when I explicitly use the coroutine dispatcher (by wrapping the handler code with withContext
), I am then able to successfully access the root context before and after making the WebClient call.
val dispatcher = RequestContext.current<RequestContext>().eventLoop().asCoroutineDispatcher()
withContext(dispatcher) {
webClient.get().uri("/200").awaitExchange { }
RequestContext.current<RequestContext>().also {
logger.debug("Request context: $it") // Success
}
ServerResponse.ok().bodyValueAndAwait("ok")
}
Is this expected behaviour? We don't see the same thing when making gRPC calls (using a FutureStub
with .await()
) - the original request context is preserved afterwards.
using a FutureStub with .await()
Could you share your code, please?
Is this expected behaviour?
I believe so. ๐ The coroutine does not have the request context after it's resumed from the suspended function so we should do the request scoping by ourselves. ๐
Is this expected behaviour?
Yes, it is possible. If the current CoroutineContext
is an EmptyCoroutineContext
, a coroutine could be changed after executing a suspend function.
/cc @okue who is good at Armeria with Kotlin Coroutine ๐
Could you share your code, please?
We have a grpc client generated with io.grpc:protoc-gen-grpc-java
, and instantiated like, for example,
val grpcClient = Clients.builder("gproto-web+http://localhost:3001")
.build(SomeServiceGrpc.SomeServiceFutureStub::class.java)
When I replace the webClient.get().awaitExchange()
in the above sample code with a grpc call grpcClient.someCall.await()
(here await
is provided by org.jetbrains.kotlinx:kotlinx-coroutines-guava
https://github.com/Kotlin/kotlinx.coroutines/blob/66fe1c8eb99ef1464248e291adfaa647e85a4e24/integration/kotlinx-coroutines-guava/src/ListenableFuture.kt#L228), the Armeria root request context is still accessible in the coroutine afterwards where it is not after a WebClient call.
And if you are using Armeriaโs annotated service with a suspend function, Armeria automatically injects RequestContext-aware CoroutineContext for you. https://armeria.dev/docs/server-annotated-service#kotlin-coroutines-support
And if you are using Armeriaโs annotated service with a suspend function
For background, we added Armeria as the underlying web server to an existing, established, Spring Webflux application built with functional routes, not annotations. While we could switch to Armeria annotations, it would be a larger refactor. If it turns out that is the most effective approach, we can tackle that work in the future.
Right now though, I'm just trying to understand the reason why the Armeria request context is accessible from a suspending function before I make a Spring WebClient call, but not available (in the same function) after making the call. I couldn't find anything in Armeria's documentation about having to manually manage scopes, I was expecting that a coroutine launched from a functional webflux endpoint served from Armeria would maintain the context throughout its life. I may well be fundamentally misunderstanding something about coroutines though ๐
While we could switch to Armeria annotations, it would be a larger refactor. If it turns out that is the most effective approach, we can tackle that work in the future
I think you don't have to. ๐ I guess @ikhoon is just telling an option.
Armeria's documentation about having to manually manage scopes,
Sorry, we need one. ๐ You might want to refer to a slide though. https://github.com/minwoox/deview2020-requestscoping/blob/master/DEVIEW2020_RequestScoping.pdf
We are running a Spring Boot WebFlux service written in Kotlin with coroutines. The Armeria Spring Boot starter has been used, with Armeria providing the
org.springframework.web.reactive.function.client.WebClient
implementation.In any handler function, we're seeing that making any HTTP call with the Armeria
WebClient
causes any subsequent calls toRequestContext.current()
throwIllegalStateException
with"RequestContext unavailable"
.We noticed the issue as we have a decorator to pass on some specific request headers from the originating request to our gRPC endpoints. When we added a WebClient call before making the gRPC calls, the decorator was no longer able to grab the headers as the context is not found.
Here's a minimal service which reproduces the issue. The first log prints the context, the second one throws
IllegalStateException
.