TomcatがPostgreSQLへの接続プールを使い果たしていた

前にTomcatなシステムの移行をしたサーバが動かなくなったということで見てみた。先方の要望としては再構築だったが、こんなエラーが出ていて、再構築しても再発するように思えたので調べてみた。

org.apache.commons.dbcp.SQLNestedException: Cannot get a connection, pool exhausted

言葉の通りなら「DBへの接続プールを使い果たしたから接続できない」って感じ。

エラーの内容で検索すると簡単に出てくる。

大抵はConnectionのclose()し忘れが原因らしい.
情報源: でーたーべーす – S(しょーもない)E(エンジニア)さんねんせいの日記.

そうか、じゃあプログラムの修正を・・・、ってプログラムの調査費なんてもらえないし、Javaは得意分野じゃないし、何の糸口もないのにいきなり無理。まー、情報源のページもプログラム修正を促してはいない。close() し忘れの接続を勝手に取り返してくれる設定がTomcatにはある。

その設定はというと、こんな感じ。

パラメータ名 説明 自分の設定
removeAbandoned 切り忘れの検知を行う true
removeAbandonedTimeout 切り忘れとみなすまでの秒数 300
logAbandoned 切り忘れを検知したらログを出力する true

さっきの引用ページと同じだけどね。ちなみに引用ページは2006年のエントリーで、Tomcatのバージョンもそれなりだと思う。server.xml に設定するってのは今も同じだけど、書き方が今風じゃないかも。いや、自分が引き継いだ(引き継いではいない)プログラムが変な書き方なのかも?自分はTomcat 6で、CentOS 6なので /etc/tomcat6/server.xml のDB接続設定を書いているところに属性として追加しました。まー、コンテナごとに設定する場合もあるようだし、プログラム内でDBへ接続する際に指定することもできるようだし、勝手にやってくれって感じだな。

そんな古い情報だとパラメータ名が変わってないか不安になったりしますが、Tomcat 7でも利用できるようなので大丈夫ですね。
Tomcat 7の新機能で何ができるようになるのか?(番外編):Commons DBCPを超えるTomcat JDBC Poolとは (1/2) – @IT.
ところで@ITってパラグラフごとにIDを付けてくれていないから引用時に場所を伝えにくいんだよね。まー、自分のブログもそうだけど。でも全てのテーブルに同じIDを付けるとかはやめて欲しい。

それは置いといて、設定値についてだけど、removeAbandonedTimeout の初期値は60秒なのだが、自分は300秒にしておいた。Webシステムなので普通は60秒もあればいいと思うのだが、そこは他人が作ったシステム、保険的に300秒にしておいた。そんな混みあうシステムでもないだろうし、ってのもある。それにログ設定も入れておいたので、再発したらプログラムの修正という方向へも舵を切れるので十分だと思っています。

以上で依頼された仕事は完了だったのですが、そもそも大して混み合わないサイトに接続プールが必要なのかって問題もある。実際、自分が得意分野とするPHPでは接続プールを利用しないことの方が多い。セッションを使用している大規模サイトでもだ。でも、それって富豪的プログラミングなのかな、とも思えたりするけど、あんましない。なぜなら問題が起こることがあるから。本件のTomcatのケースで発生したエラーもそれだ。接続プールを利用しなければ発生しなかった問題だ。

じゃあ、なぜプールするかというと、速度が欲しいから。どんだけ速くなるかというと、他人がTomcat 7とPostgreSQL 9.3で試したものを引用してしまう。

初回以降100回のコネクション作成の平均 7.73ms 0.25ms
情報源: Tomcat JDBC Connection Poolで通常のJavaプログラムからPostgreSQL 9.3に接続する – Symfoware.

プールなしで8ms程度、プールすると1ms未満。8倍以上の差がある。でも、差は7msほどだよ。画像ダウンロードの方が遅いかもしれないし、HTMLをCSSゴリゴリにされたらレンダリングに時間がかかって数秒かかることもある。なので7msを稼ぐ必要のあるサイトがどれほどあるのかは疑問です。

あ、でもOracleは接続コストが高いって言われるのでプールした方がいいのかもね。MySQLはPostgreSQLと同じか、もっと低いかも。SQLiteはローカルのファイルをいじるだけだから低いだろうな。接続コストが低い場合は頻繁に再接続しても大して遅くはならない。DBの種別に応じた選択も必要だね。