Open RyanWang0811 opened 2 years ago
/cc @evanchooly
@geoand Are there any plans to support suspend functions for @ReactiveTransactional?
We certainly should at some point.
+1 on this one. It's a pain point for using Kotlin with Panache Reactive as you are forced to go back to using the reactive APIs for anything transactional
Otherwise using suspend functions with Quarkus Reactive is a good experience.
I'll try and have a look soon
I thought we had coroutine support for Hibernate Reactive, but that seems to not be the case @evanchooly ?
@murphye @mschorsch how exactly do you envision you would use @ReactiveTransactional
with a suspend function? Do you have examples?
@geoand I am able to use Coroutines with Hibernate Reactive. This can be done with https://smallrye.io/smallrye-mutiny/1.6.0/guides/kotlin/#awaiting-a-uni-in-coroutines.
A simple (contrived) example where there is a problem with @ReactiveTransactional
and Coroutines. In this case, customerRepository
is a Hibernate Reactive Panache repository with a persist()
that returns Uni<Customer>
.
@POST
@ReactiveTransactional
suspend fun persistAndReturnId(customer: Customer): Long? {
// Do some transactional stuff
val customerPersisted: Customer = customerRepository.persist(customer).awaitSuspending()
// Do something with customerPersisted
val customerKafkaResult = emitter.send(customerPersisted).awaitSuspending()
// Do something else with customerKafkaResult
// Persisting customer and sending to Kafka were both successful
return customerPersisted.id
}
If you were to try to use this code, you would get this error:
only Uni is supported when using @ReactiveTransaction if you are running on a VertxThread
because it's a suspend fun
.
There might need to be a new annotation such as @SuspendTransactional
rather than try to make @ReactiveTransactional
work in this context.
I thought we had coroutine support for Hibernate Reactive, but that seems to not be the case @evanchooly ?
I don't think anyone's taken a look at that specifically. It's relatively new so no one's gotten to it yet.
@murphye thanks for the snippet.
In your example you could essentially do what you want with: Panache.withTransaction(() -> customerRepository.persist(customer))
.
I am mentioning this because you are already working with a Mutiny API. I think the annotation support would only make sense if we had a suspend API for Hibernate Reactive.
@geoand I think it would also make sense for the current state of Hibernate Reactive. As @murphye showed with his snippet we can easily use awaitSuspending()
to move from Hibernate Reactive Mutiny to the Coroutines world.
Of course, if the code needed to be in a single transaction is in the same method, it is straightforward to wrap it around a withTransaction
block either using Panache or plain Hibernate Reactive. But if the code needed to be in a transaction is in different methods of different components then having to use withTransaction
is a little cumbersome and @ReactiveTransactional
would make sense, imho.
For example:
class OrdersService(private val userRepository: UserRepository, private val ordersRepository: OrdersRepository) {
@ReactiveTransactional
suspend fun createOrderForUser(user: User, order: Order) {
userRepository.createUser(user)
ordersRepository.createOrder(order)
}
}
As the above example does not work yet with Coroutines I have to introduce withTransaction
in my Service Layer, which unfortunately leads to not simple and very readable code
@akoufa thanks for the input.
But with the current state of things userRepository.createUser(user)
is not a suspend function, is it?
@geoand Yes it is a suspending function. Internally in the createUser(user)
method a call to Panache or Hibernate Reactive is made and then awaited with awaitSuspending
. So we can have a fully Kotlin Coroutines solution with Uni
hidden only in the Data Layer components without being exposed in the APIs.
I suppose we could have a new annotation named @SuspendTransaction
(or something) that would essentially call HR's transaction APIs in the proper sequence...
@geoand, Just out of curiosity: Why would you need a different annotation? Could it not just work with the normal @ReactiveTransactional
and determine the implementation based on the function's type (Uni result vs suspend function)?
It could potentially be the same annotation but it would be a lot more involved than what you describe
OK. Sorry, I assumed it is quite complex but was just wondering about this. Thanks for all your efforts you put into Quarkus and I'm totally looking forward for this feature :)
Sure, no problem.
This definitely requires more thought, but it certainly seems like an interesting thing to have.
I am mentioning this because you are already working with a Mutiny API. I think the annotation support would only make sense if we had a suspend API for Hibernate Reactive.
Just a note. There does exist a Spring CoroutineCrudRepository.kt interface with a Mongo implementation. It's something to consider when it comes to Reactive Panache.
However, I am not sure how much value a Coroutine Repository really has, as I would rather use one Reactive Repository interface and decide myself whether I want to use a suspend fun
or not depending on the implementation context. It should not be either one or the other. It's easy enough to use .awaitSuspending()
when I want to.
That being said, the @ReactiveTransactional
support is still needed for suspend fun
per my example above.
Indeed, thanks for the update
One problem of having something like @ReactiveTransactional
is that we would likely only be able to support placing it on known coroutine execution entrypoints - like JAX-RS methods, and not in arbitrary places like one can do with @Transactional
.
kotlin.coroutines.ContinuationInterceptor
might be useful, but I didn't find any good examples with a quick search.
That will be awesome if reactive transactions could be managed in the following way
@ReactiveTransactional
override suspend fun createUser(command: SignUpRequest): AuthSessionFacts {
val facts = userService.createUser(command) // suspended execution
createPersonalProfile(command, facts) // suspended execution
val session = authSessionService.createSession(facts.user, command.deviceId) // suspended execution
eventBus.publish(SIGNUP.name, UserEventData(facts.user, session))
return facts
}
One problem of having something like
@ReactiveTransactional
is that we would likely only be able to support placing it on known coroutine execution entrypoints - like JAX-RS methods, and not in arbitrary places like one can do with@Transactional
.kotlin.coroutines.ContinuationInterceptor
might be useful, but I didn't find any good examples with a quick search.
@geoand : Why is that? Aren't you already detecting all @ReactiveTransactionals with a uni response type? Wouldn't it be possible to extend this to detect if it is a coroutine and then wrap it with code to handle the transaction?
That wouldn't be enough
Any update about this feature?
Nothing new to report
On Thu, Sep 14, 2023, 23:48 Mohamed El Gabbouch @.***> wrote:
Any update about this feature?
— Reply to this email directly, view it on GitHub https://github.com/quarkusio/quarkus/issues/25563#issuecomment-1720132900, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABBMDP43Y2T7MQLIUXD7LNDX2NUTHANCNFSM5V3PXQHQ . You are receiving this because you were mentioned.Message ID: @.***>
Description
I use quarkus with kotlin suspend function to write the reactive programming.
When I try to use quarkus-hibernate-reactive-panache and make the method in a transaction boundary by using
@ReactiveTransactional
like thishttps://github.com/aotter/quarkus-examples/blob/8f4292c7ac0f4a2af70836d7bcbc28f04902b6e4/hibernate-reactive-panache-quickstart-kotlin/src/main/kotlin/net/aotter/FruitResource.kt#L56-L61
I got the exception
only Uni is supported when using @ReactiveTransaction if you are running on a VertxThread
Is it possible to let the
@ReactiveTransactional
support kotlin suspend function?Implementation ideas
No response