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
Add a jooq-kotlin-coroutines module to support transactional coroutines based on reactive streams Publishers #9335
Comments
I think this shouldn't even be too nasty from a dependency perspective -- simply mark the kotlin stdlib and coroutine dependencies as |
We have a new module starting from jOOQ 3.14: |
Looking into this now. It seems that the main purpose of the suggested approach is to replace our call to At least, that seems to be it for these |
I don't want to rush anything here (as with reactive support, too: #6298). There seems to be a significant investment, making all sorts of API |
My initial desire was to be able to call Use case:
I agree that probably any closure-flavored API like that would benefit from taking a |
Exactly. I think this is an all-or-nothing discussion. Supporting coroutines only in a few areas will not be enough. It's never ending, too. Because coroutines are not the same thing as asynchronous calls (e.g. using I personally have high hopes that Loom will clean this up for all JVM languages. These things shouldn't be solved on an API level. What's the take on Loom in the kotlin ecosystem? |
Re: Loom, I couldn't speak for the whole Kotlin community, but at least year's JCrete nobody seemed very optimistic. Even if it was shipping today, I at least would still prefer coroutines: APIs like coroutine context and scoped concurrency make them (to my mind) a better eventual goal than "threads, but more lightweight". We use coroutine context to propagate things like "current request id" across fanned out coroutines, something that requires clumsy and error prone manual work to do with threads and threadlocals. |
I personally think that Loom lacks the marketing and hype that other asynchronous and/or reactive APIs received for reasons I still don't fully understand.
I think this part of Loom is the most widely misunderstood. Loom is much more than "just" about lightweight threads. What it means specifically for the entire ecosystem and adoption is still ... looming, because it develops much slower than API based approaches, and there are no "success stories" yet. (See above comment on marketing and hype). However, the idea that asynchronous models, reactive models, and suspending models would all be possible on existing APIs (at least, that's the promise of Loom) seems very interesting, because the ultimate show-stopper for any other approach that is the currently blocking JDBC API could be re-used, and with it, an entire ecosystem of proven technology. At least Oracle has been betting on this, and thus has abandoned ADBA. What I meant by my previous doubts is that by using any of the other API alternatives, jOOQ has kept offering a wishy-washy solution to integrating jOOQ in the programming models (e.g. async via Add to that yet another approach, which are Kotlin co-routines, which attempt to solve the problems again on an API level, on a per-function basis. What is being requested here is again to be able to check some box on a superficial level, because in order to truly offer Kotlin coroutine support, I'm afraid the entirety of jOOQ's API would have to be offered in a variety of "red functions" flavours (see: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function) Having said so, if a user is happy accepting suspending Kotlin functions to fulfil programming model needs, without really getting the benefit throughout the stack, including within the database, then it would be much easier to just wrap jOOQ API calls (the query building) in some
I'm not sure what passing around context has to do with the thread model, though? For example, with the existing jOOQ transaction API, it would be possible to implement suspension in a jOOQ // Using Java syntax...
// Here, we're in some thread/fiber/execution context 1
ctx1.transactionResult(ctx2 -> {
// Here, we're transparently in some thread/fiber/execution context 2
ctx2.dsl().select(...).fetch();
}); We're not offering this out of the box yet, but again, it should be possible to achieve using SPIs. It's not easy to do (it's probably easier to use a third party library for suspending transaction support), but I'm trying to argue against the various API based solutions to these asynchronous execution models, which ripple through the entire stack, making existing tools like jOOQ or JDBC hard to use. |
Related topic: #6298 |
I think we might be talking past each other... I'm all in favor of Oracle (and Ron, who is very capable) pressing forward on Loom. It's just not all that relevant to me right now, even if it may end up being the bees knees for when it ships. In projects I touch, I don't have a need for jOOQ (or JDBC) to be all nonblocking under the hood because with typical small DB connection pool pool sizes, it's no burden to maintain a similarly small thread pool to handle blocking i/o on those connections. While it would be nice, I suppose, to avoid that, it's not really an issue in the same way that HTTP clients and servers really benefit from multiplexing many connections onto a few threads: it is more typical to have 1000 (or 100,000) HTTP connections slowly trickling data back and forth than it is to do the same with a database, at least in my experience. I don't follow how using suspending code in a TransactionalCallable could be addressed via SPI since TransactionalCallable's run() isn't suspending... what am I missing? |
I understand you'd like a quick win. My point is, jOOQ likely won't offer one for the reasons I've mentioned. Here's an example of an extremely simple, alternative transaction manager that can be used with jOOQ: https://github.com/witoldsz/ultm Here's another example of someone doing similar things for jOOQ/ZIO: If the problem this feature request is trying to solve is isolated out of jOOQ and into a dedicated library like the above, then it would be really simple to implement. Adding support for Kotlin While this seems like a simple thing for you (and it probably is), it is absolutely not for jOOQ.
I was not discussing a Kotlin specific way of doing this. I was trying to hint at how this could be achieved with a Loom style suspension... |
I totally understand your reluctance to sprinkle |
@marshallpierce Do you mind sharing the workaround you are using ? I'm on the same situation and not sure I fully understood what you are suggesting |
I browsed the PR, there is a
|
@hantsy How does the latter work? Is that an extension method on |
Reactor provides Kotlin extensions for ReactiveStreams and Reactor API. |
I've looked into these things now. Adding these dependencies: <dependency>
<groupId>io.projectreactor.kotlin</groupId>
<artifactId>reactor-kotlin-extensions</artifactId>
<version>1.1.5</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core-jvm</artifactId>
<version>1.5.2</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-reactor</artifactId>
<version>1.5.2</version>
</dependency> It seems possible to do something like this: public suspend fun x(): Int = coroutineScope {
ctx.insertInto(T4).columns(T4.T4_ID, T4.T3_ID, T4.VAL).values(1, 1, "a").awaitSingle()
} That seems to be sufficient in terms of bridging between the two worlds (reactive and coroutines). People will want to have reactive transactions in coroutine form, too, which currently isn't easy with jOOQ. It's necessary to revert to using R2DBC transaction API directly. It's a pending task here: #11717 As such, I think there's nothing more to do for jOOQ. The recommendation is:
|
I'm adding some coroutine demo code to the new demo here: https://github.com/jOOQ/demo Can confirm, it's really as simple as adding those |
OK, I get it now. Some dogfooding never hurts. After implementing that demo and answering this question here: https://stackoverflow.com/a/72457496/521799, I can tell that even with the So, how about a
Specifically for transactions, it can be annoying to write this all the time: suspend fun mySuspendFunction(jooqContext: DSLContext): Any {
return jooqContext.transactionPublisher { config ->
// Turn the suspension result into a Mono, which implements the reactive
// streams Publisher<T> SPI, which jOOQ expects as a result from a
// TransactionalPublishable
mono {
anotherSuspendFunction(config)
}
}
// Turn the Publisher<T> that is returned from transactionPublisher() back
// into a suspension result
.awaitFirst()
} Probably better: suspend fun mySuspendFunction(jooqContext: DSLContext): Any {
return jooqContext.transactionCoroutine { config ->
anotherSuspendFunction(config)
}
} |
In principle, the static final class BlockingTransactionSubscription<T> extends AbstractSubscription<T> {
final DSLContext ctx;
final TransactionalPublishable<T> transactional;
BlockingTransactionSubscription(
DSLContext ctx,
Subscriber<? super T> subscriber,
TransactionalPublishable<T> transactional
) {
super(subscriber);
this.ctx = ctx;
this.transactional = transactional;
}
@Override
final void request0() {
try {
subscriber.onNext(ctx.transactionResult(c -> block(transactional.run(c))));
subscriber.onComplete();
}
catch (Throwable t) {
subscriber.onError(t);
}
}
} |
I still think that the bridge libraries are sufficient for |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as resolved.
This comment was marked as resolved.
In case anyone stumbles on this issue and uses the code from comment #9335 (comment), please beware that code has a big issue in that it makes blocking calls (assuming the underlying driver is a standard JDBC driver) in a suspending function. For example, This will almost certainly cause hangs in a Kotlin application under load that uses coroutines, as those calls will block threads in the This variation ensures those blocking calls are run on the IO dispatcher instead:
|
Documentation has been added: https://www.jooq.org/doc/latest/manual/sql-building/kotlin-sql-building/kotlin-coroutines/ |
It should be possible to add some Kotlin extensions directly in the jOOQ core library and ship them to users integrating with Kotlin, without affecting Java users. For example, we could support coroutines as
DSLContext
extensions orResultQuery
extensionsA proof of concept has been made here:
marshallpierce@72b0b3b
As suggested on the user group:
https://groups.google.com/d/msg/jooq-user/NS5anAYmIgI/FlV3H6gvAwAJ
The text was updated successfully, but these errors were encountered: