Skip to content
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

IsAutoCommit = false & transactionIsolation = "Transaction_Serializable" result in a PSQLException #1575

Closed
AlbRoehm opened this issue Aug 22, 2022 · 16 comments · Fixed by #1949
Assignees

Comments

@AlbRoehm
Copy link

Following test will always fail with a PSQLException : Cannot change transaction isolation level in the middle of a transaction
Changing the autoCommit = false to true will make this behavior disappear, but since what if have read so far, autoCommit should always be set to false for exposed.
This behavior was not present for version 0.37.3

Testcase:

import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import kotlinx.coroutines.*
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
import org.jetbrains.exposed.sql.transactions.transaction
import org.junit.jupiter.api.*
import java.sql.Connection
import java.util.concurrent.*
import javax.sql.DataSource

@ExperimentalCoroutinesApi
@DelicateCoroutinesApi
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class DatebaseTest {

    private val mainThreadSurrogate = newSingleThreadContext("Coroutine thread")

    @BeforeAll
    fun setUp() {
        Dispatchers.setMain(mainThreadSurrogate)
    }

    @AfterAll
    fun tearDown() {
        Dispatchers.resetMain() // reset the main dispatcher to the original Main dispatcher
        mainThreadSurrogate.close()
    }

    @Test
    fun `Create a new Connection after it was closed, PSQLException`() = testCoroutine {
        val db = DatabaseMock(EnvironmentService())

        db.getPSQLVersion()
        delay(TimeUnit.SECONDS.toMillis(31))
        db.getPSQLVersion()
    }

    private suspend fun DatabaseMock.getPSQLVersion() = this.dbQuery {
        exec("SELECT VERSION();") { it.next(); it.getString(1) }
    }
}

class DatabaseMock(envVars: IEnvironmentService) : IDatabase {

    private val ISOLATION_LEVEL = "TRANSACTION_SERIALIZABLE"
    private val DRIVER_NAME = "org.postgresql.Driver"

    init {
        setupDatasource(envVars)
    }

    private fun setupDatasource(envVars: IEnvironmentService): DataSource {
        val dataSource = hikari(envVars)
        val database = org.jetbrains.exposed.sql.Database.connect(dataSource)
        // database.transactionManager.defaultIsolationLevel = Connection.TRANSACTION_SERIALIZABLE

        transaction(Connection.TRANSACTION_SERIALIZABLE, 1) {
            val schema = Schema(envVars["DATABASE_SCHEMA"], envVars["DATABASE_USER"])
            SchemaUtils.createSchema(schema)
            SchemaUtils.setSchema(schema)
        }
        return dataSource
    }

    private fun hikari(envVars: IEnvironmentService): HikariDataSource {
        val hikariConfig = HikariConfig().apply {
            driverClassName = DRIVER_NAME
            jdbcUrl = envVars["DATABASE_URL"]
            schema = envVars["DATABASE_SCHEMA"]
            username = envVars["DATABASE_USER"]
            password = envVars["DATABASE_PASSWORD"]
            maximumPoolSize = 5
            maxLifetime = 30020
            isAutoCommit = false
            transactionIsolation = ISOLATION_LEVEL
        }
        hikariConfig.validate()

        return HikariDataSource(hikariConfig)
    }

    override suspend fun <T> dbQuery(block: suspend Transaction.() -> T): T =
        newSuspendedTransaction(
            Dispatchers.IO,
            transactionIsolation = Connection.TRANSACTION_SERIALIZABLE
        ) {
            block()
        }
}

gradle.build.kts:

val exposedVersion = "0.39.2"

dependencies {

    implementation("org.jetbrains.exposed:exposed-core:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-jodatime:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion")
    implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion")

    implementation("com.zaxxer:HikariCP:5.0.1")
    implementation("org.postgresql:postgresql:42.3.6")
@AlbRoehm
Copy link
Author

Anyone maybe has an idea how to handle this situation? Not upgrading the package anymore does not seem to good. Maybe i'm also just doing it wrong? Any hints appreciated. Thanks.

@Tapac
Copy link
Contributor

Tapac commented Nov 12, 2022

@AlbRoehm few questions:

  1. Is the test fails without Dispatchers.setMain(mainThreadSurrogate) (with a default dispatcher)?
  2. Is the test fails without transactionIsolation = ISOLATION_LEVEL in hikari function?
  3. Is the test fails with transactionIsolation = Connection.TRANSACTION_SERIALIZABLE in hikari function?

@AlbRoehm
Copy link
Author

AlbRoehm commented Nov 14, 2022

Hi @Tapac,

I did the following tests with exposedVersion=0.40.1

  1. Setting Dispatchers.setMain(Dispatchers.Default) does not affect the test outcome. Test fails immediately.
  2. Test is still failing, either on transaction(Connection.TRANSACTION_SERIALIZABLE, 1) in setupDatasource or after 31s if i remove the isolation level in that call.
  3. transactionIsolation = Connection.TRANSACTION_SERIALIZABLE is not possible since hikari expects a string. transactionIsolation = "8" will give that same result as 2.

@AlbRoehm
Copy link
Author

The only way to change the outcome of the test was isAutoCommit = true or not setting it since it defaults to true. This result in hikari reset it after every use of the connection.
2022-11-14 15:02:09.096 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.pool.PoolBase - HikariPool-1 - Reset (autoCommit) on connection org.postgresql.jdbc.PgConnection@7e8140d0

Tapac added a commit that referenced this issue Nov 14, 2022
@Tapac
Copy link
Contributor

Tapac commented Nov 14, 2022

Found possible cause. I will ask you to check if the problem is gone after the next release (possibly today)

@Tapac
Copy link
Contributor

Tapac commented Nov 14, 2022

Please check 0.41.1 version and let me know if the issue was solved

@AlbRoehm
Copy link
Author

Hey, sorry to inform you that the problem is still not fixed.

If i use transaction(Connection.TRANSACTION_SERIALIZABLE, 1) in setupDatasource() it still breaks immediately with:

2022-11-15 14:27:38.226 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Pool stats (total=1, active=0, idle=1, waiting=0)
2022-11-15 14:27:38.245 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@578a0128
2022-11-15 14:27:38.273 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@594df5a9
2022-11-15 14:27:38.300 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@5d726a58
2022-11-15 14:27:38.324 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@1870ff92
2022-11-15 14:27:38.340 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - After adding stats (total=5, active=0, idle=5, waiting=0)
2022-11-15 14:27:38.406 [Coroutine thread @coroutine#2] WARN  Exposed - Transaction attempt #0 failed: Cannot change transaction isolation level in the middle of a transaction.. Statement(s): null
org.postgresql.util.PSQLException: Cannot change transaction isolation level in the middle of a transaction.
	at org.postgresql.jdbc.PgConnection.setTransactionIsolation(PgConnection.java:970)
	at com.zaxxer.hikari.pool.ProxyConnection.setTransactionIsolation(ProxyConnection.java:420)
	at com.zaxxer.hikari.pool.HikariProxyConnection.setTransactionIsolation(HikariProxyConnection.java)
	at org.jetbrains.exposed.sql.statements.jdbc.JdbcConnectionImpl.setTransactionIsolation(JdbcConnectionImpl.kt:60)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManager$ThreadLocalTransaction$connectionLazy$1.invoke(ThreadLocalTransactionManager.kt:82)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManager$ThreadLocalTransaction$connectionLazy$1.invoke(ThreadLocalTransactionManager.kt:75)
	at kotlin.UnsafeLazyImpl.getValue(Lazy.kt:81)
	at org.jetbrains.exposed.sql.transactions.ThreadLocalTransactionManager$ThreadLocalTransaction.getConnection(ThreadLocalTransactionManager.kt:89)
	at org.jetbrains.exposed.sql.Transaction.getConnection(Transaction.kt)
	at org.jetbrains.exposed.sql.Database.metadata$exposed_core(Database.kt:38)
	at org.jetbrains.exposed.sql.Database$vendor$2.invoke(Database.kt:44)
	at org.jetbrains.exposed.sql.Database$vendor$2.invoke(Database.kt:43)
	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
	at org.jetbrains.exposed.sql.Database.getVendor(Database.kt:43)
	at org.jetbrains.exposed.sql.Database$dialect$2.invoke(Database.kt:48)
	at org.jetbrains.exposed.sql.Database$dialect$2.invoke(Database.kt:47)
	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
	at org.jetbrains.exposed.sql.Database.getDialect(Database.kt:47)
	at org.jetbrains.exposed.sql.vendors.DefaultKt.getCurrentDialect(Default.kt:893)
	at org.jetbrains.exposed.sql.Schema.exists(Schema.kt:40)
	at org.jetbrains.exposed.sql.SchemaUtils.createSchema(SchemaUtils.kt:550)
	at org.jetbrains.exposed.sql.SchemaUtils.createSchema$default(SchemaUtils.kt:547)
	at com.example.DatabaseMock$setupDatasource$1.invoke(DatebaseTest.kt:64)

if i setupDatabase() only with transaction() and no arguments the test fails after 31 seconds when hikari is opening new connections with:

org.jetbrains.exposed.exceptions.ExposedSQLException: org.postgresql.util.PSQLException: Cannot change transaction isolation level in the middle of a transaction.

Even if i don't define the TRANSACTION_SERIALIZABLE level no where in my code (not in transaction, not in dbQuery, not in hikariConfig) the test is still failing after 31s with `org.postgresql.util.PSQLException: Cannot change transaction isolation level in the middle of a transaction.

2022-11-15 14:38:30.599 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.HikariConfig - poolName........................"HikariPool-1"
2022-11-15 14:38:30.599 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.HikariConfig - readOnly........................false
2022-11-15 14:38:30.600 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.HikariConfig - registerMbeans..................false
2022-11-15 14:38:30.600 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.HikariConfig - scheduledExecutor...............none
2022-11-15 14:38:30.600 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.HikariConfig - schema.........................."test_schema"
2022-11-15 14:38:30.600 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.HikariConfig - threadFactory...................internal
2022-11-15 14:38:30.600 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.HikariConfig - transactionIsolation............default
2022-11-15 14:38:30.600 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.HikariConfig - username........................"username"
2022-11-15 14:38:30.601 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.HikariConfig - validationTimeout...............5000
2022-11-15 14:38:30.602 [Coroutine thread @coroutine#2] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
2022-11-15 14:38:30.838 [Coroutine thread @coroutine#2] INFO  com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@3483016
2022-11-15 14:38:30.842 [Coroutine thread @coroutine#2] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
2022-11-15 14:38:30.946 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Pool stats (total=1, active=1, idle=0, waiting=0)
2022-11-15 14:38:30.959 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@19db5fb2
2022-11-15 14:38:30.967 [Coroutine thread @coroutine#2] DEBUG c.zaxxer.hikari.pool.ProxyConnection - HikariPool-1 - Executed rollback on connection org.postgresql.jdbc.PgConnection@3483016 due to dirty commit state on close().
2022-11-15 14:38:30.973 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - After adding stats (total=2, active=0, idle=2, waiting=0)
2022-11-15 14:38:31.279 [Coroutine thread @coroutine#2] DEBUG Exposed - SET search_path TO smbc_processor
2022-11-15 14:38:31.317 [DefaultDispatcher-worker-1 @coroutine#3] DEBUG Exposed - SELECT VERSION();
2022-11-15 14:39:00.411 [HikariPool-1 connection closer] DEBUG com.zaxxer.hikari.pool.PoolBase - HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@3483016: (connection has passed maxLifetime)
2022-11-15 14:39:00.424 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@33b5a67a
2022-11-15 14:39:00.849 [HikariPool-1 connection closer] DEBUG com.zaxxer.hikari.pool.PoolBase - HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@19db5fb2: (connection has passed maxLifetime)
2022-11-15 14:39:00.863 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@3d724e7c
2022-11-15 14:39:00.956 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Pool stats (total=2, active=0, idle=2, waiting=0)
2022-11-15 14:39:00.957 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Fill pool skipped, pool has sufficient level or currently being filled (queueDepth=0).
2022-11-15 14:39:02.342 [DefaultDispatcher-worker-1 @coroutine#4] DEBUG Exposed - SELECT VERSION();
2022-11-15 14:39:02.343 [DefaultDispatcher-worker-1 @coroutine#4] WARN  Exposed - Transaction attempt #1 failed: org.postgresql.util.PSQLException: Cannot change transaction isolation level in the middle of a transaction.. Statement(s): SELECT VERSION();
org.jetbrains.exposed.exceptions.ExposedSQLException: org.postgresql.util.PSQLException: Cannot change transaction isolation level in the middle of a transaction.
	at org.jetbrains.exposed.sql.statements.Statement.executeIn$exposed_core(Statement.kt:49)
	at org.jetbrains.exposed.sql.Transaction.exec(Transaction.kt:141)
	at org.jetbrains.exposed.sql.Transaction.exec(Transaction.kt:127)
	at org.jetbrains.exposed.sql.Transaction.exec(Transaction.kt:109)
	at org.jetbrains.exposed.sql.Transaction.exec$default(Transaction.kt:97)
	at com.example.DatebaseTest$getPSQLVersion$2.invokeSuspend(DatebaseTest.kt:44)

Still only changing to isAutoCommit = true solves the issue with following output:

2022-11-15 14:49:47.087 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.HikariConfig - transactionIsolation............"TRANSACTION_SERIALIZABLE"
2022-11-15 14:49:47.087 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.HikariConfig - username........................"username"
2022-11-15 14:49:47.087 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.HikariConfig - validationTimeout...............5000
2022-11-15 14:49:47.088 [Coroutine thread @coroutine#2] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
2022-11-15 14:49:47.334 [Coroutine thread @coroutine#2] INFO  com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@5c966d9
2022-11-15 14:49:47.338 [Coroutine thread @coroutine#2] INFO  com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
2022-11-15 14:49:47.442 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Pool stats (total=1, active=0, idle=1, waiting=0)
2022-11-15 14:49:47.458 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@6089113
2022-11-15 14:49:47.469 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - After adding stats (total=2, active=0, idle=2, waiting=0)
2022-11-15 14:49:47.780 [Coroutine thread @coroutine#2] DEBUG Exposed - SET search_path TO smbc_processor
2022-11-15 14:49:47.790 [Coroutine thread @coroutine#2] DEBUG com.zaxxer.hikari.pool.PoolBase - HikariPool-1 - Reset (autoCommit) on connection org.postgresql.jdbc.PgConnection@5c966d9
2022-11-15 14:49:47.832 [DefaultDispatcher-worker-1 @coroutine#3] DEBUG Exposed - SELECT VERSION();
2022-11-15 14:49:47.834 [DefaultDispatcher-worker-1 @coroutine#3] DEBUG com.zaxxer.hikari.pool.PoolBase - HikariPool-1 - Reset (autoCommit) on connection org.postgresql.jdbc.PgConnection@5c966d9
2022-11-15 14:50:16.802 [HikariPool-1 connection closer] DEBUG com.zaxxer.hikari.pool.PoolBase - HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@5c966d9: (connection has passed maxLifetime)
2022-11-15 14:50:16.817 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@455560d5
2022-11-15 14:50:17.059 [HikariPool-1 connection closer] DEBUG com.zaxxer.hikari.pool.PoolBase - HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@6089113: (connection has passed maxLifetime)
2022-11-15 14:50:17.072 [HikariPool-1 connection adder] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@4b92960f
2022-11-15 14:50:17.451 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Pool stats (total=2, active=0, idle=2, waiting=0)
2022-11-15 14:50:17.452 [HikariPool-1 housekeeper] DEBUG com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Fill pool skipped, pool has sufficient level or currently being filled (queueDepth=0).
2022-11-15 14:50:18.858 [DefaultDispatcher-worker-1 @coroutine#4] DEBUG Exposed - SELECT VERSION();
2022-11-15 14:50:18.859 [DefaultDispatcher-worker-1 @coroutine#4] DEBUG com.zaxxer.hikari.pool.PoolBase - HikariPool-1 - Reset (autoCommit) on connection org.postgresql.jdbc.PgConnection@455560d5

@Tapac
Copy link
Contributor

Tapac commented Nov 15, 2022

Hmm. it looks like select version() instantiate transaction before setting new transaction level.
Will try to understand where the request comes from (most likely Hikary checks the connection for rediness with it)

@AlbRoehm
Copy link
Author

AlbRoehm commented Nov 15, 2022

Also i think the problem was introduced sometime between 1.1.2022 and release of 0.38.1 where i first started to see it to happen.

@AlbRoehm
Copy link
Author

I use the select version() in a healthCheck endpoint to check if database is ready. I thought it is viable to use it in this test but i just tried the same test with a table insertion and the error is still happening.

@Test
    fun `Create a new Connection after it was closed, PSQLException`() = testCoroutine {
        val db = DatabaseMock(EnvironmentService())

        createEntry()
        delay(TimeUnit.SECONDS.toMillis(31))
        createEntry()
    }

    private suspend fun getPSQLVersion() = dbQuery {
        exec("SELECT VERSION();") { it.next(); it.getString(1) }
    }

    private suspend fun createEntry() = dbQuery {
        RandomEntity.new { test = UUID.randomUUID().toString() }
    }
}

object RandomTable: IntIdTable(){
    var test = text("text")
}

class RandomEntity(id: EntityID<Int>): IntEntity(id){
    companion object : IntEntityClass<RandomEntity>(RandomTable)
    
    var test by RandomTable.test
}

suspend fun <T> dbQuery(dispatcher: CoroutineDispatcher = Dispatchers.IO, block: suspend Transaction.() -> T): T =
    newSuspendedTransaction(
        dispatcher,
        transactionIsolation = Connection.TRANSACTION_SERIALIZABLE
    ) { block() }

class DatabaseMock(envVars: IEnvironmentService) : IDatabase {

    private val isolationLevel = "TRANSACTION_SERIALIZABLE"
    private val driverName = "org.postgresql.Driver"

    init {
        setupDatasource(envVars)
    }

    private fun setupDatasource(envVars: IEnvironmentService) {
        val dataSource = hikari(envVars)
        org.jetbrains.exposed.sql.Database.connect(dataSource)
        transaction(Connection.TRANSACTION_SERIALIZABLE, 1) {
            val schema = Schema(envVars["DATABASE_SCHEMA"], envVars["DATABASE_USER"])
            SchemaUtils.createSchema(schema)
            SchemaUtils.setSchema(schema)
            SchemaUtils.create(RandomTable)
        }
    }

    private fun hikari(envVars: IEnvironmentService): HikariDataSource {
        val hikariConfig = HikariConfig().apply {
            driverClassName = driverName
            jdbcUrl = envVars["DATABASE_URL"]
            schema = envVars["DATABASE_SCHEMA"]
            username = envVars["DATABASE_USER"]
            password = envVars["DATABASE_PASSWORD"]
            maximumPoolSize = 2
            isAutoCommit = false
            maxLifetime = 30020
            transactionIsolation = isolationLevel
        }
        hikariConfig.validate()

        return HikariDataSource(hikariConfig)
    }
}

@Tapac
Copy link
Contributor

Tapac commented Nov 15, 2022

Is it really fails with same transaction level set into hikari config and into transaction/newSuspendedTransaction ?

@Tapac
Copy link
Contributor

Tapac commented Nov 15, 2022

I've go the exception when I have different isolation levels and (!) when schema is defined on pool.
It looks like Hikari executes 'set path` (or something) to set default schema and it initiates the transaction with pool level, so Exposed can't change it after that.

Can you, just of curious, try the same code with that driver?

@AlbRoehm
Copy link
Author

Is it really fails with same transaction level set into hikari config and into transaction/newSuspendedTransaction ?

It fails with the same transaction level in hikari and in transaction. But i got the feeling that either one is setting it not, otherwise i don't get why hikari wants to reset the connection.
Sorry for the missing code parts, i have setup a full and complete example containing everything here.: https://github.com/AlbRoehm/ExposedErrorMinimalExample

@AlbRoehm
Copy link
Author

AlbRoehm commented Nov 16, 2022

Can you, just of curious, try the same code with that driver?

I tried that driver and the test did not fail, but if i understand it correctly there is also no connection pool involved that closes and reopens connections. So I'm not sure if it is comparable.

@AlbRoehm
Copy link
Author

AlbRoehm commented Nov 16, 2022

I think i found a pretty interesting thing that is related to the hikariConfig.schema property. I seems like there is something going wrong when hikari is creating the connection when the schema is set. It might be the case that with setting the schema property the transaction is already opened?

So i created seperate files for this case and i checked:

  • Case1: Setting schema only via SchemaUtils.setSchema() and not in HikariConfig + isAutoCommit = false:

    • Did not result in an error! But schema is set at the beginning of every transaction.
  • Case2: Setting schema only via HikariConfig:

    • if autoCommit is enabled there is no error but connection is reset everytime.
  • CASE 3 Setting schema only in hikari and using different transaction_level for creating table and writing to it.

    • Outcome: no error but schema of hikari config is ignored, table is created in public schema and config is ignored.

    I added tests for these 3 cases in the repo

@AlbRoehm
Copy link
Author

AlbRoehm commented Nov 16, 2022

After looking some more into the schema issue i found this ticket in the hikari repo. brettwooldridge/HikariCP#1633
I tried the described the workaround and removed every other logic that did something with the schema.
That seemed to have resolved the problem. To be honest i don't fully understand but i think setting the schema is already part of a opened transaction?
Maybe it is possible to add one or two lines to the docs about this? I think i might just "used hikari & exposed" wrong if this is the fix, because is did not find much information on how to set it up correctly when one needs to use a schema.

In case you still want to have a look, i added a test for this scenario too in the repo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants