Quarkus: Kotlin Coroutines Breaks Jackson Serialization

Created on 19 Mar 2020  路  9Comments  路  Source: quarkusio/quarkus

Describe the bug
See https://github.com/sherl0cks/kotlin-coroutines-quarkus-graal-jackson-reproducer/blob/master/README.md

It is possible that this should be opened up in https://github.com/oracle/graal. If so, I would appreciate some assistance on that from the Quarkus team as you all have been very responsive and probably have better contacts with Graal than I do.

Expected behavior
Jackson works in coroutines like it does without coroutines.

Actual behavior
Some variation of the below exception depending on the versions of libraries:

2020-03-19 15:02:43,528 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /greeting/normal failed, error id: 68a91fde-fc23-44cd-a4e2-3d9735b85543-1: org.jboss.resteasy.spi.UnhandledException: java.lang.IllegalStateException: @NotNull method kotlin/reflect/jvm/internal/impl/builtins/KotlinBuiltIns.getBuiltInClassByFqName must not return null

To Reproduce
See https://github.com/sherl0cks/kotlin-coroutines-quarkus-graal-jackson-reproducer/

Environment (please complete the following information):
See https://github.com/sherl0cks/kotlin-coroutines-quarkus-graal-jackson-reproducer/

Additional context
Kotlin coroutines and graal already have known issues: https://github.com/oracle/graal/issues/366

arekotlin kinbug

Most helpful comment

Whether we want to bring some co-routine friendly APIs to Mutiny...

Vert.x actually has pretty decent support for Kotlin's co-routines already and I hope it works well on GraalVM in the future. :crossed_fingers:
This way kotlin flow could also provide an alternative to mutiny

All 9 comments

@geoand 鈽濓笍

Thanks for reporting @sherl0cks.

IIUC, co-routines don't work at all on native-image, right?

If that is the case, I don't know what we can do.
Perhaps @dmlloyd has more insight

@geoand they work, but there are issues for sure. I think from a Quarkus perspective, there needs to be clear guidance to Kotlin users how to do nonblocking and async. If not coroutines, then what? Should users incorporate vert.x? Stick to completeable future without the coroutines bridge? IMO - adding this to the Kotlin guide with a strong opinion is important. Otherwise users like my team will fall into the void.

That said, when coroutines work, they are great! So it would nice to also put some pressure on oracle from the quarkus side to get it working well (see the comments in the bottom of https://github.com/oracle/graal/issues/366).

Thanks for the input @sherl0cks.

cc @emmanuelbernard @n1hility for input on how we should frame our suggestions in the guide

I'll just add that out general reactive strategy is focused on Vert.x and Mutiny

@emmanuelbernard @n1hility bumping this. It would be really nice to have clear guidance on the kotlin guide here. I'm happy to submit a PR, but I don't know how you want to frame this. Also worth noting that there is now progress with Graal and kotlin coroutines which should be released in Graal 20.1 https://github.com/oracle/graal/issues/1330.

The other item that came to mind is the way the AWS lambda integration is written, and my understanding is that it is blocking by design (e.g. cannot handle multiple lambda events at once, unlike the native Node.js lambda runtime). So @patriot1burke may have input there.

On the co-routine aspect I would frame it as such. GraalVM and Kotlin co-routines are work in progress blah blah. You can use them in Quarkus apps but native executable support is hit and miss. The Quarkus way to do reactive is via Mutiny and vert.x as it brings more to the table than co-routines. See https://blog.softwaremill.com/will-project-loom-obliterate-java-futures-fb1a28508232 for a great in depth article.
(please rephrase but that's the gist)

Whether we want to bring some co-routine friendly APIs to Mutiny (if that is even a thing), I'd defer to @evanchooly (with @cescoffier guidance).

Whether we want to bring some co-routine friendly APIs to Mutiny...

Vert.x actually has pretty decent support for Kotlin's co-routines already and I hope it works well on GraalVM in the future. :crossed_fingers:
This way kotlin flow could also provide an alternative to mutiny

Stumbled upon this issue as I ran into a similar problem. After a bit of testing, it appears that it is not related to coroutines at all, but rather that the Jackson Kotlin module is missing or broken in the native build. If the code can be made to run without the jackson kotlin module, it works both on jvm and native. With and without coroutines.

A simple example, without coroutines, that works on jvm and fails native:

import javax.ws.rs.Consumes
import javax.ws.rs.POST
import javax.ws.rs.Path
import javax.ws.rs.Produces
import javax.ws.rs.core.MediaType

data class Person(
    val name: String,
    val address: Address
)

data class Address(
    val street: String
)

@Path("/hello")
class ExampleResource {

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    fun hello(person: Person) = person
}

The error message presented when trying the native build, is the same as one would get on jvm if the kotlin module for jackson is removed.

The above code can be made to run without the kotlin module by changing the Address class to

data class Address(
    @JsonProperty("street")
    val street: String
)

In which case the native build also works.

If the code is wrapped in a coroutine, the actual error is hidden by the kotlin reflect stuff.

Hopefully you are able to get the jackson kotlin module working also in a native build :)

Was this page helpful?
0 / 5 - 0 ratings