question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using blocking transactions after a suspending transaction causes a "Connection is closed" exception

See original GitHub issue

I’m not sure if it is intentional, I tried searching about it on the Wiki, FAQ and in the issues and I couldn’t find anything about it. The only similar issues that I’ve found was #680 and #601

Of course, you shouldn’t mix blocking and suspending methods, after all, why would you do that? The reason I found out about it is that I only recently found out that Exposed does have coroutine-aware methods (whoops) and I started migrating away from the blocking transactions to the suspended transactions, so I changed a few of my transactions to suspended transactions… then I found out that you can’t mix them, oops.

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.jetbrains.exposed.dao.id.LongIdTable
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SchemaUtils
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
import org.jetbrains.exposed.sql.transactions.transaction
import kotlin.concurrent.thread

fun main() {
	val database = Database.connect("jdbc:postgresql://127.0.0.1:5432/teste", driver = "org.postgresql.Driver",
			user = "postgres", password = "postgres")

	transaction(database) {
		SchemaUtils.createMissingTablesAndColumns(
				OriginalTable
		)
	}

	GlobalScope.launch {
		println("Starting...")

		val transaction1 = newSuspendedTransaction(Dispatchers.IO, database) {
			OriginalTable.selectAll()
					.toList()
		}

		println("Coroutine finished")

		val transaction2 = transaction(database) {
			OriginalTable.selectAll()
					.toList()
		}

		println("Done!")
	}

	thread {
		while (true) {
			Thread.sleep(1_000)
		}
	}
}

object OriginalTable : LongIdTable() {
	val testNumber = integer("test_number")
}
10:32:30.445 [main] INFO Exposed - Preparing create tables statements took 403ms
10:32:30.463 [main] INFO Exposed - Executing create tables statements took 14ms
10:32:30.609 [main] INFO Exposed - Extracting table columns took 146ms
10:32:31.210 [main] INFO Exposed - Extracting column constraints took 600ms
10:32:31.210 [main] INFO Exposed - Preparing alter table statements took 747ms
10:32:31.211 [main] INFO Exposed - Executing alter table statements took 1ms
10:32:31.229 [main] INFO Exposed - Checking mapping consistence took 18ms
Starting...
10:32:31.386 [DefaultDispatcher-worker-1] DEBUG Exposed - SELECT original.id, original.test_number FROM original
Coroutine finished
Exception in thread "DefaultDispatcher-worker-2" org.jetbrains.exposed.exceptions.ExposedSQLException: org.postgresql.util.PSQLException: This connection has been closed.
SQL: [SELECT original.id, original.test_number FROM original]
	at org.jetbrains.exposed.sql.statements.Statement.executeIn$exposed_core(Statement.kt:50)
	at org.jetbrains.exposed.sql.Transaction.exec(Transaction.kt:126)
	at org.jetbrains.exposed.sql.Transaction.exec(Transaction.kt:112)
	at org.jetbrains.exposed.sql.Query.iterator(Query.kt:212)
	at kotlin.collections.CollectionsKt___CollectionsKt.toCollection(_Collections.kt:1200)
	at kotlin.collections.CollectionsKt___CollectionsKt.toMutableList(_Collections.kt:1233)
	at kotlin.collections.CollectionsKt___CollectionsKt.toList(_Collections.kt:1224)
	at com.mrpowergamerbr.loritta.listeners.TestKt$main$2$transaction2$1.invoke(Test.kt:36)
	at com.mrpowergamerbr.loritta.listeners.TestKt$main$2$transaction2$1.invoke(Test.kt)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt$transaction$1.invoke(ThreadLocalTransactionManager.kt:121)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.keepAndRestoreTransactionRefAfterRun(ThreadLocalTransactionManager.kt:212)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.transaction(ThreadLocalTransactionManager.kt:113)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManagerKt.transaction(ThreadLocalTransactionManager.kt:111)
	at com.mrpowergamerbr.loritta.listeners.TestKt$main$2.invokeSuspend(Test.kt:34)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
	at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
	at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Caused by: org.postgresql.util.PSQLException: This connection has been closed.
	at org.postgresql.jdbc.PgConnection.checkClosed(PgConnection.java:857)
	at org.postgresql.jdbc.PgConnection.prepareStatement(PgConnection.java:1763)
	at org.postgresql.jdbc.PgConnection.prepareStatement(PgConnection.java:410)
	at org.postgresql.jdbc.PgConnection.prepareStatement(PgConnection.java:1777)
	at org.jetbrains.exposed.sql.statements.jdbc.JdbcConnectionImpl.prepareStatement(JdbcConnectionImpl.kt:58)
	at org.jetbrains.exposed.sql.statements.Statement.prepared(Statement.kt:25)
	at org.jetbrains.exposed.sql.statements.Statement.executeIn$exposed_core(Statement.kt:48)
	... 19 more

The code works fine if:

  1. They are both blocking transactions
  2. They are both coroutine-aware transactions

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

2reactions
MrPowerGamerBRcommented, May 11, 2020

This seems to happen when a coroutine shares the same thread as another thread that had an connection before.

If you create your own coroutine dispatcher based on a fixed thread pool of 16 threads, this bug happens only sometimes.

If you change your thread pool to only 1 thread, this bug happens every time.

What looks like what it is happening is that the transaction context is leaking to the original coroutine. (the GlobalScope.launch { part), if you create a dispatcher with only one thread it breaks every single time)

0reactions
MrPowerGamerBRcommented, May 17, 2020

I’ve done some small and quick tests so I’m not sure if it is 100% fixed, but looks like it was fixed! Thanks @Tapac :3

Read more comments on GitHub >

github_iconTop Results From Across the Web

Understand and resolve SQL Server blocking problems
Find the query and transaction that is causing the blocking (what is holding locks for a prolonged period)
Read more >
Handling java.sql.SQLRecoverableException: Closed ...
This error message indicates that the connection used by the backup task (or any other long-running operation that relies on a single database...
Read more >
JDBC distributed transactions - IBM
When you end a transaction, all open ResultSets that were created under that transaction automatically close. It is recommended that you explicitly close...
Read more >
what does suspending a transaction means? - Stack Overflow
When a transaction is suspended , it waits until it can pick up where it left off. This means, the changes that happened...
Read more >
Spring Transaction Management: @Transactional In-Depth
The proxy has access to a transaction manager and will ask it to open and close transactions / connections. The transaction manager itself...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found