suspend fun inTopLevelSuspendTransaction()

in exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/v1/jdbc/transactions/Transactions.kt [314:382]


suspend fun <T> inTopLevelSuspendTransaction(
    db: Database? = null,
    transactionIsolation: Int? = db?.transactionManager?.defaultIsolationLevel,
    readOnly: Boolean? = db?.transactionManager?.defaultReadOnly,
    outerTransaction: JdbcTransaction? = null,
    statement: suspend JdbcTransaction.() -> T
): T {
    var attempts = 0
    var intermediateDelay: Long = 0
    var retryInterval: Long? = null

    val database = resolveDatabaseOrThrow(db)

    while (true) {
        val transaction = database.transactionManager.newTransaction(
            transactionIsolation ?: database.transactionManager.defaultIsolationLevel,
            readOnly ?: database.transactionManager.defaultReadOnly,
            outerTransaction
        )

        try {
            @OptIn(InternalApi::class)
            return withTransactionContext(transaction) {
                try {
                    executeTransactionWithErrorHandling(transaction, shouldCommit = true) {
                        transaction.db.config.defaultSchema?.let { SchemaUtils.setSchema(it) }
                        transaction.statement()
                    }
                } catch (cause: SQLException) {
                    handleSQLException(cause, transaction, attempts)
                    throw cause
                }
            }
        } catch (cause: SQLException) {
            attempts++

            if (retryInterval == null) {
                retryInterval = transaction.getRetryInterval()
                intermediateDelay = transaction.minRetryDelay
            }

            val retryDelay = when {
                transaction.minRetryDelay < transaction.maxRetryDelay -> {
                    intermediateDelay += retryInterval * attempts
                    ThreadLocalRandom.current().nextLong(intermediateDelay, intermediateDelay + retryInterval)
                }

                transaction.minRetryDelay == transaction.maxRetryDelay -> transaction.minRetryDelay
                else -> 0
            }
            exposedLogger.debug("Wait $retryDelay milliseconds before retrying")

            try {
                Thread.sleep(retryDelay)
            } catch (cause: InterruptedException) {
                // Do nothing
            }

            if (attempts >= transaction.maxAttempts) {
                throw cause
            }
        } finally {
            @OptIn(InternalApi::class)
            withTransactionContext(transaction) {
                closeStatementsAndConnection(transaction)
            }
        }
    }
}