Open OrdinarySF opened 1 year ago
/cc @DavideD (hibernate-reactive), @FroMage (panache), @Sanne (hibernate-reactive), @evanchooly (kotlin), @gavinking (hibernate-reactive), @geoand (kotlin), @loicmathieu (panache)
maven dependency:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kotlin</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-config-yaml</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-redis-client</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-panache-kotlin</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-kotlin</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>kotlin-extensions</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-openapi</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-reactive-panache-kotlin</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-reactive-pg-client</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>${mapstruct.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
<scope>provided</scope>
</dependency>
Can you add a sample project that behaves as you mention?
This will work if you change the function to:
@GET
@Path("/{id}")
@WithSession
fun getSingle(id: Long): Uni<User> {
return userRepository.getById(id)
}
In Quarkus, reactive functions are expected to return a Uni
.
I don't think you are supposed to suspend them in this scenario (it's should already been taken care of).
But I don't know enough about Kotlin to know if there's a difference.
The problem is almost certainly due to the improper use of the Vert.x context when awaitSuspending
is used.
There is not much that can be done without a proper integration
@DavideD @geoand In the quarkus guide, there is mention of support for the Kotlin coroutine, so I would like to try fully coroutinized reactive development. Like this issus.
I tried to add @WithSession
directly but it didn't work. So how can I integrate appropriate dependencies to support this feature.
Coroutines are supported on a per feature manner. We need to integrate them into with Hibernate Reactive - cc @evanchooly
@GET
@Path("/{id}")
suspend fun getSingle(id: Long): User = coroutineScope {
Panache.withSession {
this.async {
// Put your actual code here
return@async userRepository.findById(id).awaitSuspending()
}.asUni()
}.awaitSuspending()
}
This should work but it's quite annoying to have to wrap the code like that.
You can create an utilitary function for convenience:
@OptIn(ExperimentalCoroutinesApi::class)
suspend fun <T> session(block: suspend () -> T): T = coroutineScope {
Panache.withSession {
this.async { block() }.asUni()
}.awaitSuspending()
}
@GET
@Path("/{id}")
suspend fun getSingle(id: Long): User = session {
userRepository.findById(id).awaitSuspending()
}
Yeah, that's something we don't want people to have to do - cc @evanchooly
I can see where we make that leap in to the coroutine context but I'm not familar with the vert.x context bits so I'm not sure what to do here.
@Feavy Great, that it can work. But maybe we have a better solution to reduce the overhead of context switching? This is a question worth discussing.
There is also a similar issue when trying to return a Uni<T>
(for example in IdentityProvider
) and to make the coroutine remember the session.
I managed to get a solution as @Feavy to work which essentially does the same setup as the AbstractCoroutineInvoker
that Quarkus uses when invoking suspending functions. The trick is to use the correct coroutine scope and dispatcher.
@OptIn(ExperimentalCoroutinesApi::class)
fun <R> withPanacheTransaction(block: suspend () -> R): Uni<R> = Panache.withTransaction {
val coroutineScope = Arc.container().instance(ApplicationCoroutineScope::class.java).get()
val dispatcher: CoroutineDispatcher =
io.vertx.core.Vertx.currentContext()?.let(::VertxDispatcher)
?: throw IllegalStateException("No Vertx context found")
coroutineScope.async(dispatcher) {
block()
}.asUni()
}
Sadly, both features were not designed to be composed. "Do want a suspend function endpoint? We've got you" "Do want to use kotlin with Panache? Don't you worry my man" "You want both? Erh, no can do"
It's quite gruesome to work with both of these (I had to abandon panache)
@FredyH could you please provide a gist of your solution and a small sample implementation for withPanacheTransaction?
The implementation you can find in my post above, and you can use it like the following in a controller:
@GET
@Path("/{id}")
suspend fun getSingle(id: Long): Uni<User> = withPanacheTransaction {
userRepository.findById(id).awaitSuspending()
}
However, please note that I stopped using Panache myself because there were far too few pain points when using it with coroutines, so I am not sure that you will not run into other issues along the way.
@FredyH So what library would you recommend when it comes to Kotlin Coroutines and reading entities from a database, e.g. Postgres?
Are you using io.quarkus:quarkus-reactive-pg-client
for this purpose, because here they state that this should work out of the box with suspend functions: https://github.com/quarkusio/quarkus/issues/35359#issuecomment-1688782251
Unfortunately this is really low level api without JPA etc.
So I would be interested what others are using.
@SimonScholz Personally, we switched back to using Jooq with their new coroutines support and quarkus plugin. It worked for us because we encountered the issue early on in the project. Unfortunately, it is probably not an option for most people :(
Describe the bug
Expected behavior
No response
Actual behavior
error:
How to Reproduce?
No response
Output of
uname -a
orver
Microsoft Windows [Version 10.0.22621.1992]
Output of
java -version
openjdk version "17.0.7" 2023-04-18 LTS OpenJDK Runtime Environment GraalVM 22.3.2 (build 17.0.7+7-LTS) OpenJDK 64-Bit Server VM GraalVM 22.3.2 (build 17.0.7+7-LTS, mixed mode, sharing)
GraalVM version (if different from Java)
22.3.2
Quarkus version or git rev
3.2.1.Final
Build tool (ie. output of
mvnw --version
orgradlew --version
)Apache Maven 3.9.3 (21122926829f1ead511c958d89bd2f672198ae9f) Maven home: E:\client\apache-maven-3.9.3 Java version: 17.0.7, vendor: BellSoft, runtime: D:\Program Files\BellSoft\bellsoft-liberica-vm-full-openjdk17-22.3.2 Default locale: zh_CN, platform encoding: GBK OS name: "windows 11", version: "10.0", arch: "amd64", family: "windows"
Additional information
No response