https://kotlinlang.org logo
#exposed
Title
# exposed
m

maxmello

10/24/2019, 9:18 AM
Inside a Ktor post { … } I have the following code:
Copy code
val dao = newSuspendedTransaction { MyDAO.findByValueOrThrowException(value) }

// MyDAO companion (LongEntityClass)
 suspend fun findByValueOrThrowException(value: String): MyDAO {
        return withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
                find { MyTable.value eq value }.firstOrNull() ?: throw RuntimeException("MyDAO not found")
      }
}
10 seconds after the exception is thrown, I get the following log message: com.zaxxer.hikari.pool.ProxyLeakTask - Connection leak detection triggered for org.postgresql.jdbc.PgConnection@5b4d25e7 on thread DefaultDispatcher-worker-1, stack trace follows: java.lang.Exception: Apparent connection leak detected As you can see, I am using Hikari for a thread pool (config.leakDetectionThreshold = 10000L, corresponding to the 10 seconds after the message appears) So am I not allowed to throw an exception and catch it outside the transaction context, or am I doing something else wrong? (I am using Postgres)
s

spand

10/24/2019, 9:23 AM
I havent used
newSuspendedTransaction
yet but why do you change context ?
m

maxmello

10/24/2019, 9:26 AM
You can also provide the context directly with newSuspendedTransaction(Dispatchers.IO) { … }, I did it this way to ensure that when my suspend fun is called, it is always called in the IO Dispatcher and not accidentally on another one.
s

spand

10/24/2019, 9:27 AM
What makes you think its not safe to do in any dispatcher ?
I would heavily expect Exposed to ensure any blocking sql calls to take place on the correct dispatcher and not whever is the current one
m

maxmello

10/24/2019, 9:48 AM
I’m not sure, as you can see in the example above from Evan R., they also specify the Dispatcher explicitly. But I wouldn’t think that this would be the problem.
Here is a better display of what code causes the problem and which not
Fix seems to be to wrap everything inside the suspended block inside a try / catch, and in the catch block call this.close() (of Transaction) manually and rethrow the exception afterwards.
e

Evan R.

10/24/2019, 12:53 PM
@spand exposed coroutine support is currently experimental according to the docs, but newSuspendedTransaction will always start a transaction in the current coroutine context unless a different one is specified explicitly
s

spand

10/24/2019, 12:56 PM
I am sorry if I am just making noise here but I would expect this to work:
Copy code
val myDAO = newSuspendedTransaction { find { MyTable.value eq value }.firstOrNull() } ?: throw RuntimeException()
e

Evan R.

10/24/2019, 12:58 PM
It does work, but the calls inside newSuspendedTransaction will block threads of the current coroutine context you’re running in if you don’t specify the dispatcher. Better to explicitly specify to run in the IO dispatcher to avoid blocking the default dispatcher’s thread pool
e

Evan R.

10/24/2019, 1:45 PM
tx.statement() in TransactionScope.suspendedTransactionAsyncInternal As stated at https://github.com/JetBrains/Exposed/wiki/Transactions: “Please note [that transactions invoked inside newSuspendedTransaction] remains blocking (as it still uses JDBC)”
Even though the blocking calls are invoked inside an async block, they still block the thread they’re running on and Exposed never changes the coroutine context
3 Views