question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[question] using interceptor to populate coroutine context from listener

See original GitHub issue

Hello!

Was wondering if there is any way to populate coroutine context from the interceptor listeners, i.e. we would like to populate additional MDC elements based on the incoming request and afaik we can only access those from the listener.

class CustomServerInterceptor : ServerInterceptor {
    override fun <ReqT : Any, RespT : Any> interceptCall(call: ServerCall<ReqT, RespT>, metadata: Metadata, next: ServerCallHandler<ReqT, RespT>): ServerCall.Listener<ReqT> {
        return object: ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(next.startCall(call, metadata)) {
            override fun onMessage(message: ReqT) {
                // populate MDC here
                super.onMessage(message)
            }
        }
    }
}

Above doesn’t work as listeners are invoked after processing all the interceptors. This means that context created by CoroutineContextServerInterceptor.interceptCall() will use snapshot of values up to that point and even if we modify the MDC in the listeners, those changes will be discarded when performing rpc call on the service (as it will start running a coroutine using the context from the interceptor).

Using workaround of creating custom coroutine context passed to the constructor (https://github.com/grpc/grpc-kotlin/issues/66) also doesn’t work as it appears that bindable service context will be initialized before invoking the listeners as well.

The only other workaround I can think of is to manually populate the MDC from within the RPC methods, e.g.

class HelloWorld : HelloWorldAPIGrpcKt.HelloWorldAPICoroutineImplBase() {
    override suspend fun helloWorld(request: HelloWorldRequest): HelloWorldResponse {
        MDC.put("foo", "bar")
        return withContext(MDCContext()) {
            HelloWorldApiProtoBuilders.HelloWorldResponse {
                helloWorldString = "Hi ${request.id}"
            }
        }
    }
}

is there any better way?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:6

github_iconTop GitHub Comments

2reactions
lowassercommented, Nov 16, 2020

Yes, I missed that, my bad.

Given that, your described approach is as good as it gets at this time. There’s a related issue, that’s kind of skimmed over in your example code’s comments:

            override fun onMessage(message: ReqT) {
                // populate MDC here
                super.onMessage(message)
            }

In this block, message has a completely unknown type. An interceptor is generally supposed to handle any method: any request type, streaming or unary requests. In the code you gave, you’d have to cast message to do anything with it.

1reaction
dariuszkuccommented, Nov 16, 2020

@lowasser I might be missing something but how would I get access to the request message in custom context interceptor?

class CustomContextServerInterceptor(private val context: CoroutineContext = EmptyCoroutineContext) : CoroutineContextServerInterceptor() {

    override fun coroutineContext(call: ServerCall<*, *>, headers: Metadata): CoroutineContext {
        // populate MDC values based on metadata -> this works fine
        MDC.put("myKey", headers.get(Metadata.Key.of("myKey", Metadata.ASCII_STRING_MARSHALLER))
        // how do i populate MDC based on the incoming request?
        MDC.put("request", call?)
        return Dispatchers.Default + MDCContext() + context
   }
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Custom interceptors in coroutines - Stack Overflow
The first line shows you what the behavior is supposed to be, and the second one shows what it is with your implementation....
Read more >
Simplifying APIs with coroutines and Flow | by Manuel Vivo
This article covers how to simplify APIs using coroutines and Flow as well as how to build your own adapter using ...
Read more >
Coroutine Context and Dispatchers | Baeldung on Kotlin
Thread management: ContinuationInterceptor, which listens to the continuation within a coroutine and intercepts its resumption.
Read more >
Going Reactive with Spring, Coroutines and Kotlin Flow
Spring MVC or WebFlux? These questions are obviously highly subjective and usually depend on the project context, but I will share my ...
Read more >
Table of Contents - Micronaut Documentation
You can enforce a subset of constraints using validation groups using groups on the @Validated . Advanced Listener Configuration. Micronaut framework 3.5.0 ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found