TomcatのDBコネクションが8個までしか増えない謎を追う ～ あるOSSサポートエンジニアの1日


**「TomcatのDBコネクションが8個しかできないんだけど、なんで？？」**

そんな質問があり、原因を調査しました。意外なオチだったので、そのときの記録をここに残します。

この質問とともに受け取った`context.xml`には以下のような定義がありました。

<!-- ![q.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/81f1eb38-e5f0-1ed1-d496-465f13846176.png) -->

```xml
<Resource name="jdbc/MyDB"
          auth="Container"
          type="javax.sql.DataSource"
          driverClassName="com.mysql.jdbc.Driver"
          factry="org.apache.tomcat.jdbc.pool.DataSourceFactory"
          url="jdbc:mysql://dbserver.example.com:3306/db4app"
          username="dbuser"
          password="xxxxxxxxxx"
          initialSize="20"
          maxActive="20" />
```

Tomcatを起動したら、コネクションプールにコネクションが20個できると思ったのに、8個しかできないとのこと。確かにこの設定であれば、`initialSize`と`maxActive`が`20`なので、起動中に20個のコネクションが生成されて、その後も20個のまま増減しないはずです。

![quest.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/f2cf0440-dd93-89a5-d82f-e14e03c6b885.png)

## 問題を整理する

この質問を受けて、まず疑問に思ったのは「質問者がどのようにコネクション数を確認したのか？」ということです。何をもとに「8個しかできない」と判断したのかは不明確だったので、それについて聞いてみることにしました。

すると、以下のコマンドをDB（MySQL）に発行して、確認しているとのこと。

```
mysql> show processlist;
+------+--------+---------------------------------+--------+---------+------+-------+------------------+
| Id   | User   | Host                            | db     | Command | Time | State | Info             |
+------+--------+---------------------------------+--------+---------+------+-------+------------------+
| 1142 | root   | localhost                       | NULL   | Query   |    0 | NULL  | show processlist |
| 1614 | dbuser | application.example.co.jp:37110 | db4app | Sleep   |  746 |       | NULL             |
| 1615 | dbuser | application.example.co.jp:37111 | db4app | Sleep   |  746 |       | NULL             |
| 1616 | dbuser | application.example.co.jp:37112 | db4app | Sleep   |  746 |       | NULL             |
| 1617 | dbuser | application.example.co.jp:37113 | db4app | Sleep   |  746 |       | NULL             |
| 1618 | dbuser | application.example.co.jp:37114 | db4app | Sleep   |  746 |       | NULL             |
| 1619 | dbuser | application.example.co.jp:37115 | db4app | Sleep   |  746 |       | NULL             |
| 1620 | dbuser | application.example.co.jp:37116 | db4app | Sleep   |  746 |       | NULL             |
| 1621 | dbuser | application.example.co.jp:37117 | db4app | Sleep   |  746 |       | NULL             |
+------+--------+---------------------------------+--------+---------+------+-------+------------------+
9 rows in set (0.00 sec)
```

`show processlist;`の結果にはクライアントからのコネクションの数だけレコードが含まれるので、確認の仕方は間違っていないはずです。`initialSize`が`20`なのに、アプリケーションからのコネクションを表すレコードが8件しかない（※）のは明らかにおかしいです。

<table><td>※：上記レコードは9件ですが、このうち<code>show processlist;</code>を実行するコネクションの1レコードを除くと8件になります。</td></table>

そして、次に思いついた可能性は、「そもそも見ている設定ファイルが違うのでは」ということでした。しかし、そうではないようです。というのも、`context.xml`の`initialSize`を`1`に設定すると上記コマンドは1件のレコードを返し、`5`に設定すると5件、`8`に設定すると8件のレコードを返すからです。そして、不思議なことに、`9`以上に設定しても8件のまま変化しないというのです。

 - `initialSize = "1"`：コネクション数 1 :large_blue_circle:
 - `initialSize = "3"`：コネクション数 3 :large_blue_circle::large_blue_circle::large_blue_circle:
 - `initialSize = "5"`：コネクション数 5 :large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle:
 - `initialSize = "8"`：コネクション数 8 :large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle:
 - `initialSize = "9"`：コネクション数 8 :large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle:
 - `initialSize ="20"`：コネクション数 8 :large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle::large_blue_circle:
 
つまり、`context.xml`の`initialSize`が効いていることは間違いないのですが、`maxActive`ではない何かが8件を超えるコネクションの生成を制限しているということになります。

## 試す

念の為、実際にローカル環境でも試してみることにしました。MySQLにJDBC接続する定義が既に`context.xml`に記載されているWebアプリが手元にあったので、接続URL（`url`）などの定義はそのまま残し、それ以外を受け取った`context.xml`と同じになるように修正してみました（`initialSize`と`maxActive`を`20`に修正し、残りの`minIdle`などの定義は削除）。そして、Tomcatを起動し、確認してみました。が、やはり事象は再現せず、コマンドを実行すると、20件のレコードが返ってきました。

「ということは、Tomcatではなく、MySQLサーバー側でクライアントが扱えるコネクション数を制限をしているのでは？」

MySQLサーバーが制限しているとすれば、MySQLのコマンドライン・クライアントでも同じ問題が発生するはずです。そこで、クライアントを同時に9つ起動して、MySQLサーバーに接続してみるようにお願いしてみました。すると、Tomcatのときとは異なり、コマンドは9件のレコードを返したとの回答が返ってきました。

確かにMySQL側で`max_connections`などのパラメーターで制限しているのであれば、Tomcatにはエラーが返り、ログに何らかの情報（`Too many connections`のようなメッセージ）が出力されるはずです。しかし、ログには関連しそうなメッセージが何も出力されていませんでした。

## ググる

![ggl.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/8f0230c3-6551-f7a4-51b4-6263bfa5dbc1.png)

「TomcatでもMySQLでもない？もしかして、ORマッパーやアプリに特殊な仕組みが？それともファイアウォールが切断している？」

いろいろな可能性に頭をめぐらせてみましたが、`8`というのがどうもひっかかります。そこで、「"8 connections" "initialSize" "show processlist"」のキーワードでググってみることにしました。すると、同様の現象に遭遇している人の質問がStackOverflowに見つかりました。

 - [Tomcat 8 - MySQL at most 8 connections seen in MySQL Workbench from each tomcat instace](https://stackoverflow.com/questions/34199400/tomcat-8-mysql-at-most-8-connections-seen-in-mysql-workbench-from-each-tomcat)

> - initialSize == 1 - at Tomcat startup 1 connections are created
> - initialSize == 5 - at Tomcat startup 5 connections are created
> - initialSize >= 9 - at Tomcat startup 8 connections are created

**「まさにこれだ！」**そう思ったのですが、この質問とは全く一致しない条件が1つありました。

それは、**StackOverflowの質問者が「Commons DBCP」を使用しているのに対し、我々は「Tomcat JDBC Connection Pool」を使っている**ということです。TomcatのDBコネクションプールには2種類あり、古いバージョンで使用されていた「Commons DBCP」と新しいバージョンで使用されている「Tomcat JDBC Connection Pool」があります。

最初に載せた`context.xml`を見ると分かりますが、以下の定義があると、

```
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
```

「Tomcat JDBC Connection Pool」が使用されるため、残念ながらこのページの回答にある「「Commons DBCP」の`maxTotal`が未設定であるためにデフォルトの8で制限されるので、`maxTotal`を指定しろ」という対策は、今回のケースには適用できないはずです。

とは言え、事象があまりにも似ていて、すぐにはこのStackOverflowのページから離れることができずにいましました。「「Commons DBCP」が使われているなら、つじつまがあうのに...」

## 謎が解ける

「もしかして、質問者が古いバージョンを使っている？」と一瞬思いましたが、`context.xml`とともに受け取っていたTomcatのログから、使用しているバージョンが7.0.92であることは確かなので、その可能性はありません。

```
2019/08/27 15:44:34 org.apache.catalina.startup.VersionLoggerListener log
情報: Server version:        Apache Tomcat/7.0.92
```

「Tomcat JDBC Connection Pool」が導入されたのがバージョン7であることは、何度も見た＠ITの記事（以下）で知っていました。

 - [Tomcat 7の新機能で何ができるようになるのか？（番外編）：Commons DBCPを超えるTomcat JDBC Poolとは (1/2)](https://www.atmarkit.co.jp/ait/articles/1111/07/news212.html)

他に「Commons DBCP」が使用される可能性はないのでしょうか？

「両者を切り替える定義はこの1行だけだったはず...」

```
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
```

「そして、受け取った`context.xml`にもこの定義があったはず...」

```
factry="org.apache.tomcat.jdbc.pool.DataSourceFactory"
```

「ん...んん...ん？...」:scream:

<!--![factry.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/9528d97d-4277-e27a-7b03-456cef870ffb.jpeg)
![index.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/ecfdde68-cb09-d4d5-9c8d-62bfbb80a765.png)-->
![sol.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43869/5a2e48e4-33c4-83f1-4244-62ea1c81f6ff.png)

つまり、typoによりこの定義が無視され、意図せず「Commons DBCP」が使用されてしまい、未指定の`maxTotal`のデフォルト値`8`が機能することで、コネクションは8件までしか生成されなかった、というわけです。そして、コネクションの最大数の設定は「Commons DBCP」が`maxTotal`、「Tomcat JDBC Connection Pool」が`maxActive`で異なるのに対し、コネクションの初期数の設定は`initialSize`で同じなこともこの不思議な挙動を演出していたことになります。

後で考えてみると、JMXクライアント（JConsoleなど）でコネクションプールの属性値を取得してもらった方が、`maxTotal`になっていることにもっと早く気づいたかもしれません。原因が分かってみれば、そんなことも思ったりしますが...

## 最後に

実はこのページの最初に書いた設定も`factry`となっていたんですが、気づかれた方はいたでしょうか？よくあることですが、みなさん、typoには気をつけましょう。こういう場合は、正確性の高いサイト（Tomcatの公式ドキュメントとか）からのコピペが確実ですね。

　[![cci95logo.gif](https://raw.githubusercontent.com/k-tamura/cocha-icons-9to5er-edition/master/cci95logo.gif)](https://github.com/k-tamura/cocha-icons-9to5er-edition)