pkg.go.dev - cloud.google.com/go/spannerでReadOnlyTransaction
を使ったときにハマったところについてメモ。
数万件のデータを複数回のリクエストに分けて処理するようなバッチ処理のコードを書いていた。
ReadOnlyTransaction
を使った処理を以下のように書いていた。
for {
// 〜略〜
// cは*spanner.Client
iter := c.ReadOnlyTransaction().Query(ctx, stmt)
defer iter.Stop()
// 〜略〜
}
一見問題なさそうに見えたのでバッチ処理を走らせていたのだが、特定件数を超えると処理が止まる問題が発生した。
spannerのgoクライアントにはセッション管理の仕組みがあるのだが、トランザクションの終了処理が漏れていたことにより、セッションプールが枯渇、リクエストがタイムアウトしていたらしい。 内部的にはセッション管理の仕組みがReadOnlyTransactionの実行をブロックしているような形になってしまっていたらしい。
トランザクションの終了処理を呼び出すように変更する。
for {
// 〜略〜
// cは*spanner.Client
tx := c.ReadOnlyTransaction()
defer tx.Close()
iter := tx.Query(ctx, stmt)
defer iter.Stop()
// 〜略〜
}
トランザクションの終了処理がないとトランザクション実行の度に新規セッションが生成され、セッションプールが枯渇してしまう。
処理が毎回同じ件数で止まっていたのは、SPANNER_SESSION_POOL_MAX_OPEND
の値制限に引っかかったからの模様。計算してみると帳尻が合う。
ドキュメントをちゃんと読むということ以外には、ツールを使った解決方法もある。 github.com - gcpug/zagane
後はGCPのモニタリングでcloudspannerのsession countをウォッチするというのも有りかもしれない。