1リクエストで複数コネクションを取得してはならない(当たり前)

かなりみっともない話を公開する。
この2週間ほど、24時間体制でやばいサイトのサーバ監視をやっていた。
どうやばいかというと、アクセスがピークになる時間帯に、アプリケーションサーバ(Resin3)がCPU100%になってだんまりになるのだ。
このときDBサーバ(MySQL)から接続状況を見ると、Resinのmax-connection数と同じ数の接続が張られていて、全接続がSleep状態になっている。
つまり、

  • アプリはDBに接続後、何かを全力で行っている。
  • DBは何もしていない。

という状態。
普通に考えるとアプリの中で無限ループしているということになるが、いったいどこでループしているのか。


で、Struts登場以前に書かれたジャングルみたいなソースを泣きながら読んで、やっと原因がわかりました。
サーブレットから引き渡されたコネクションを使わずに、Dao層の中で勝手に JNDI Lookup してコネクションを取得するコードが何箇所かあったのだ。
サーブレットがリクエスト処理の先頭で取得するコネクションは、レスポンスを書き出す寸前に解放される。
なので、サーブレットが問題のコードを呼び出すと、1スレッドが2本のコネクションを同時に持つことになる。
そのサーブレットがResinのmax-connection数だけ同時にリクエストを受けると、全スレッドが1本目のコネクションを取得し、2本目の取得待ちに入る。で、デッドロックすると。


1リクエスト=複数コネクションではまともにトランザクション管理ができないから、プロダクションシステムのコードとしてリリースされているはずがないのだが、このシステムはMySQL(MyISAM)なのでトランザクションの概念がなく、そんなこともあってこういうデタラメが行われていたと思われる。
徹夜で修正してリリースしたら、実にすがすがしい朝を迎えることができた。でももういやだ。