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

Request context is not preserved across Kotlin coroutines #45441

Open
LarsSven opened this issue Jan 8, 2025 · 8 comments
Open

Request context is not preserved across Kotlin coroutines #45441

LarsSven opened this issue Jan 8, 2025 · 8 comments
Labels
area/kotlin kind/bug Something isn't working

Comments

@LarsSven
Copy link
Contributor

LarsSven commented Jan 8, 2025

Describe the bug

I was working with an @RequestScoped bean and @ActivateRequestContext, however, I kept running into errors with the request scope not being active when calling my bean, even though I used @ActivateRequestContext. It turns out this happens because the request context is lost when passing to a Kotlin coroutine.

I am unsure as to whether you would want this to be supported given the thread-safe nature of RequestScoped. However, micronaut seems to have run into a similar issue and then fixed it (micronaut-projects/micronaut-core#4661). If this behaviour should not be changed, it would be nice to at least have this in the documentation as this took a lot of time to realise/figure out.

Expected behavior

No response

Actual behavior

No response

How to Reproduce?

No response

Output of uname -a or ver

No response

Output of java -version

No response

Quarkus version or git rev

No response

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

No response

Additional information

No response

@LarsSven LarsSven added the kind/bug Something isn't working label Jan 8, 2025
Copy link

quarkus-bot bot commented Jan 8, 2025

/cc @geoand (kotlin)

@geoand
Copy link
Contributor

geoand commented Jan 8, 2025

Can you please provide a sample application that shows the problem you mention?

Thanks

@geoand geoand added the triage/needs-reproducer We are waiting for a reproducer. label Jan 8, 2025
@LarsSven
Copy link
Contributor Author

LarsSven commented Jan 8, 2025

I made a code snippet to demonstrate it. If you want a full demo application let me know.

This is an example of a case where it is broken and does not work:

class CoroutineRequestContextReproducer {
    @RequestScoped
    class C {
        // Must be called from a function that has a request context due to the @RequestScoped annotation
        suspend fun sleep() {
            delay(1000)
        }
    }

    @Inject
    private lateinit var c: C

    @ActivateRequestContext
    fun a() = runBlocking {
        // This will work
        c.sleep()

        // This will not work
        async {
            // The request context is now lost, as it is not propagated to the coroutine
            b()
        }
    }

    suspend fun b() {
        c.sleep()
    }
}

This case will however work when using a coroutine :

class CoroutineRequestContextReproducer {
    @RequestScoped
    class C {
        // Must be called from a function that has a request context due to the @RequestScoped annotation
        suspend fun sleep() {
            delay(1000)
        }
    }

    @Inject
    private lateinit var c: C

    fun a() = runBlocking {
        // This will not work, because a is not annotated with @ActivateRequestContext
        c.sleep()

        // The context would have been lost here, but we declare the context within the coroutine
        async {
            b()
        }
    }

    @ActivateRequestContext
    suspend fun b() {
        /// This will now work, because b is annotated with @ActivateRequestContext and is already within the coroutine
        c.sleep()
    }
}

@geoand
Copy link
Contributor

geoand commented Jan 8, 2025

That would be helpful, thanks!

@LarsSven
Copy link
Contributor Author

LarsSven commented Jan 9, 2025

@geoand here's a simple Quarkus command app that reproduces the issue:
https://gitlab.com/l.s.andringa1/quarkus-request-context-reproducer

@geoand
Copy link
Contributor

geoand commented Jan 10, 2025

That's a great reproducer, thanks!

The problem is indeed clear (the request scope is not propagated to the coroutine thread), but as I've not looked deep at coroutines in a while, I am unsure how it's supposed to be solved.

@LarsSven
Copy link
Contributor Author

LarsSven commented Jan 10, 2025

Thanks for looking into it!
Is there any way to progress on this? As this is something we would like to use.

@geoand
Copy link
Contributor

geoand commented Jan 10, 2025

You can use something similar to what we do in Quarkus REST to support coroutines.

In there you'll see we capture the CDI scope and the context classloader in order to make everything work as expected.
It's not trivial to do, but if done properly, it should work.

@geoand geoand removed the triage/needs-reproducer We are waiting for a reproducer. label Jan 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/kotlin kind/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants