I'm currently working on a kotlin project and for the database layer I'm using jdbi (instead of an ORM I want to use something where I can write plain SQL).
I have to execute multiple statements in a transaction and the statements are already packed in suspending functions. To run multiple statements in one transaction I try to use the following code:
suspend fun <T> withinTransaction(fn: suspend (Handle) -> T): T =
jdbi.inTransaction<T, Exception> { handle ->
fn(handle)
}
This is not working, because the inTransaction handler of the jdbi library is not defined as a suspending function and because of this the compiler is complaining with the following message: Suspension functions can be called only within coroutine body.
Is there a way how I can still execute the suspending SQL statements inside a transaction, without using runBlocking?
Related
We are using quarkus to process messages this run on a regular function
in that we have to call a suspend function
basically
fun process (msg:Message):Message{
val resFrom:Data = runBlocking{fetchDataFromDB(msg.key)}
val processedMsg = processRoutingKey(msg,resFrom)
return processedMsg
}
We would like to get the data as a Uni (https://smallrye.io/smallrye-mutiny/getting-started/creating-unis)
so basically we would like to get back
fun process (msg:Message){
val resFrom:Uni<Data> = ConvertUni {fetchDataFromDB(msg.key)}
}
We need the uni further downstream one time to process some data but we would like to return a Uni from the method meaning
fun process (msg:Message):Uni<Message>{
val resFrom:Uni<Data> = ConvertUni {fetchDataFromDB(msg.key)}
val processed:Uni<Message> =process(msg,resfrom)
return processed
}
The signature fun process(msg:Message): Uni<Message> implies that some asynchronous mechanism needs to be started and will outlive the method call. It's like returning a Future or a Deferred. The function returns immediately but the underlying processing is not done yet.
In the coroutines world, this means you need to start a coroutine. However, like any async mechanism, it requires you to be conscious about where it will run, and for how long. This is defined by the CoroutineScope you use to start the coroutine, and this is why coroutine builders like async require such a scope.
So you need to pass a CoroutineScope to your function if you want it to start a coroutine that will last longer than the function call:
fun CoroutineScope.process(msg:Message): Uni<Message> {
val uniResult = async { fetchDataFromDB(msg.key) }.asUni()
return process(msg, uniResult)
}
Here Deferred<T>.asUni() is provided by the library mutiny-kotlin. In the examples given in their doc, they use GlobalScope instead of asking the caller to pass a coroutine scope. This is usually a bad practice because it means you don't control the lifetime of the started coroutine, and you may leak things if you're not careful.
Accepting a CoroutineScope as receiver means the caller of the method can choose the scope of this coroutine, which will automatically cancel the coroutine when appropriate, and will also define the thread pool / event loop on which the coroutine runs.
Now, with that in mind, you see that you'll be using a mix of coroutines and Uni at the same level of API here, which is not great. I would advise to instead stick with suspend functions all the way, until you really have to convert to Uni.
I'm working on a new side project with a goal of more deeply learning Kotlin, and I'm having a little trouble figuring out how to mix Kotlin-style concurrency with code not written with coroutines in mind (JOOQ in this case). The function below is in one of my DAOs, and in that map block, I want to update a bunch of rows in the DB. In this specific example, the updates are actually dependent on the previous one completing, so it will need to be done sequentially, but I'm interested in how this code could be modified to run the updates in parallel, since there will undoubtedly be use cases I have that don't need to be run sequentially.
suspend fun updateProductChoices(choice: ProductChoice) = withContext(Dispatchers.IO) {
ctx().transaction { config ->
val tx = DSL.using(config)
val previousRank = tx.select(PRODUCT_CHOICE.RANK)
.from(PRODUCT_CHOICE)
.where(PRODUCT_CHOICE.STORE_PRODUCT_ID.eq(choice.storeProductId))
.and(PRODUCT_CHOICE.PRODUCT_ID.eq(choice.productId))
.fetchOne(PRODUCT_CHOICE.RANK)
(previousRank + 1..choice.rank).map { rank ->
tx.update(PRODUCT_CHOICE)
.set(PRODUCT_CHOICE.RANK, rank - 1)
.where(PRODUCT_CHOICE.PRODUCT_ID.eq(choice.productId))
.and(PRODUCT_CHOICE.RANK.eq(rank))
.execute()
}
}
}
Would the best way be to wrap the transaction lambda in runBlocking and each update in an async, then awaitAll the result? Also possibly worth noting that the JOOQ queries support executeAsync() which returns a CompletionStage.
Yes, use JOOQ's executeAsync. With executeAsync, you can remove the withContext(Dispatchers.IO) because the call is no longer blocking.
The kotlinx-coroutines-jdk8 library includes coroutines integration with CompletionStage, so you can do a suspending await on it (docs).
To perform the updates in parallel, note that the same library can convert a CompletionStage to a Deferred (docs). Therefore, if you change the call to execute to executeAsync().asDeferred() you will get a list of Deferreds, on which you can awaitAll().
Currently, I have the following in my code
fun use(consumer: (T) -> Unit) {
consumer(this.value)
}
suspend fun useS(consumer: suspend (T) -> Unit) {
consumer(this.value)
}
These are 2 methods that are actually doing the same. However, I was not able to merge them into one, nor to use overloaded method. In some places my consumer argument is a regular function, on other places it is suspend function; I do not have control over that.
Is it possible to have just one method, regardless of my consumer "suspendability"?
EDIT: forgot to mention that this.value is private and hence using inline would not work - still, I am in control of that, so might change the visibility of the value field.
IF the code really is as simple as you've provided, simply using the non-suspend version and make it inline would solve your issues.
By making it inline (and thus inlining the consumer) it allows the inner block to use the calling environment of the caller. This is why all the library helper functions like also can be used in a suspend function and call suspend functions without explicitly being suspend functions themselves.
I am fairly new to Kotlin, and am getting to grips with it's implementation of co-routines. I understand that any function that we may want Kotlin to deal with in a non-blocking way needs to be annotated with suspend, and that such functions can only be executed within a co-routine (or within another suspend function). So far so good.
However I keep coming across a problem with utility functions that accept other functions as parameters. For instance with arrow's Try:
suspend fun somethingAsync() = 1 + 1
Try { 1 + 1 } // All is well
Try { somethingAsync() } // Uh oh....
As the parameter to Try's invoke function/operator is not annotated with suspend, the second call will be rejected by the compiler. How does someone deal with this when writing utility functions that can not know if the code inside the passed function or lambda requires suspend or not? Writing a suspend and a non-suspend version of every such function seems incredibly tedious. Have I missed an obvious way to deal with this situation?
First, let's deal with suspend. What it means is this particular function blocks. Not that this function is asynchronous.
Usually, blocking means IO, but not always. In your example, the function doesn't block, nor does it something in an asynchronous manner (hence Async suffix is incorrect there). But lets assume actual utility code does block for some reason.
Now dealing with suspending functions is something that is done on the caller side. Meaning, what would you like to do while this is being executed:
fun doSomething() {
Try { somethingAsync() }
}
If you're fine with doSomething to block, then you can use runBlocking:
fun doSomething() = runBlocking {
Try { somethingAsync() }
}
I have a list of Job instances which I want to cancel at some point after launch. This looks as follows:
val jobs = arrayListOf<Job>()
//launch and add jobs...
jobs.forEach { it.cancelAndJoin() } // cancels the jobs and waits for completion
Unfortunately, it's not possible to use a method reference here. The reason: cancelAndJoin is a suspend function, as the compiler complains:
jobs.forEach (Job::cancelAndJoin)
"Error:(30, 24) Kotlin: Unsupported [Callable references to suspend functions]"
Why doesn't this work?
UPD: This has already been implemented in Kotlin 1.3.x. Taking a callable reference to a suspending function gives you an instance of KSuspendFunctionN (N = 0, 1, ...). This type has its invoke operator defined as a suspending function, so that you can invoke such a callable reference suspending a coroutine in the same way as a direct invocation would.
Basically, supporting this requires an additional portion of language design and does not simply come bundled with coroutines.
Why is it non-trivial? Because when you take a callable reference of an ordinary function e.g. String::reversed, you get something like a KFunction1<String, String>. If you could do the same with a suspend function, what would you expect to get?
If it's the same KFunctionN<...>, then there's an obvious problem that you can pass it around where an ordinary function is expected and call it, violating the rule that suspend functions can only be called inside coroutines (where the compiler transforms their call sites).
So, it should be something more specific. (I'm currently only speculating, without any idea of actual design attempts) It could be, for example, a SuspendKFunctionN<...>, with its invoke(...) being a suspending function, or it could (less likely) be a special notation only for passing a function reference where a suspend (T) -> R is expected, but anyway, a feature like this requires thorough design to be future-proof.
These helpers currently lack in Kotlin Standard library, but you can implement your own.
For example:
suspend fun <T> Iterable<T>.forEachAsync(action: suspend (T) -> Unit): Unit {
val list = this.map { e ->
async(...) {
action(e)
}
}
list.forEach { it.await() }
}
However, what context to pass to async now depends on the threading model your service is using (i.e. do you want to do multi-threading or want to keep everything in a single thread).