in exposed-jdbc/src/main/kotlin/org/jetbrains/exposed/v1/jdbc/transactions/Transactions.kt [167:235]
fun <T> inTopLevelTransaction(
db: Database? = null,
transactionIsolation: Int? = db?.transactionManager?.defaultIsolationLevel,
readOnly: Boolean? = db?.transactionManager?.defaultReadOnly,
outerTransaction: JdbcTransaction? = null,
statement: 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 withThreadLocalTransaction(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)
withThreadLocalTransaction(transaction) {
closeStatementsAndConnection(transaction)
}
}
}
}