きっかけ
処理上どうやっても、1回しか実行されないはずの処理が3回実行されていて怪現象発生か?という事件がありました。
再現のコード
JetBrains謹製のKotlinのSQLライブラリExposedを使って、transaction単位での処理をしてました。
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回だから気を付けてねという話でした。