takeda_san’s blog

KotlinとVRを頑張っていく方向。

ExposedのtransactionでSQL関連の例外が出るとリトライされますよ

きっかけ

処理上どうやっても、1回しか実行されないはずの処理が3回実行されていて怪現象発生か?という事件がありました。

再現のコード

JetBrains謹製のKotlinのSQLライブラリExposedを使って、transaction単位での処理をしてました。

github.com

import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.transactions.transaction
import java.sql.SQLException

fun main() {
    Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
    var counter = 0

    transaction {
        println("transaction開始!: $counter")

        when (counter) {
            0 -> println("0に入ったよ")
            1 -> println("1に入ったよ")
            2 -> println("2に入ったよ")
            3 -> println("3に入ったよ")
        }

        counter++
        throw SQLException("Exception発生!:$counter")
    }
}

これを実行すると、こうなる。
普通ならループでも何でもないので、 0に入ったよ の次の1や2は実行されないはず。

transaction開始!: 0
0に入ったよ
transaction開始!: 1
1に入ったよ
transaction開始!: 2
2に入ったよ
Exception in thread "main" java.sql.SQLException: Exception発生!:3
    at main.MainKt$main$1.invoke(Main.kt:22)

出るんだなこれが。
when句というか、transactionに囲まれた部分が3回実行されてますね。

そうだよ、やっぱりリトライだよ

原因はExposedのtransactionで行われているリトライでした。
実装を見に行きましょう。

Exposed/ThreadLocalTransactionManager.kt at master · JetBrains/Exposed · GitHub

この辺で、transaction内の処理でSQLExceptionが発生すると何かログを出して、再処理してるっぽい雰囲気を感じる。
引数でもらっている repetitionAttempts がリトライ上限っぽい。

そして、公式ドキュメントを見に行くとtransactionの引数で、トランザクション境界とリトライ回数が指定できるらしい。

Transactions · JetBrains/Exposed Wiki · GitHub

import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.transactions.transaction
import java.sql.Connection
import java.sql.SQLException

fun main() {
    Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
    var counter = 0

    transaction(Connection.TRANSACTION_SERIALIZABLE, 2) {
        println("transaction開始!: $counter")

        when (counter) {
            0 -> println("0に入ったよ")
            1 -> println("1に入ったよ")
            2 -> println("2に入ったよ")
            3 -> println("3に入ったよ")
        }

        counter++
        throw SQLException("Exception発生!:$counter")
    }
}

実行結果がこちら。
無事リトライが2回に変わっていますね。

transaction開始!: 0
0に入ったよ
transaction開始!: 1
1に入ったよ
Exception in thread "main" java.sql.SQLException: Exception発生!:2
    at main.MainKt$main$1.invoke(Main.kt:23)

ということで、Exposedのtransactionのリトライはデフォルトで3回だから気を付けてねという話でした。

リポジトリ

github.com