Groovy でもコネクションのクローズを忘れずに。

Groovy ラクチンだなー♪ と思いつつ、サーバー上でGroovyを動かしていたら、以下のエラー。

2008/11/01 18:21:38 org.apache.catalina.core.StandardWrapperValve invoke
致命的: サーブレット HogeServlet のServlet.service()が例外を投げました
java.lang.IllegalStateException: com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException: Data source rejected establishment of connection,  message from server: "Too many connections"
	at OfWadsServlet.service(OfWadsServlet.java:35)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
	at java.lang.Thread.run(Thread.java:619)
Caused by: com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException: Data source rejected establishment of connection,  message from server: "Too many connections"
	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:921)
	at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1070)
	at com.mysql.jdbc.Connection.createNewIO(Connection.java:2775)
	at com.mysql.jdbc.Connection.<init>(Connection.java:1555)
	at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:285)
	at java.sql.DriverManager.getConnection(DriverManager.java:582)
	at java.sql.DriverManager.getConnection(DriverManager.java:185)
	at groovy.sql.Sql.newInstance(Sql.java:136)
	at groovy.sql.Sql.newInstance(Sql.java:157)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
	at groovy.lang.MetaClassImpl.invokeStaticMethod(MetaClassImpl.java:1105)
	at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:749)
	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodN(ScriptBytecodeAdapter.java:170)
	at DatabaseResource.init(DatabaseResource.groovy:9)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:86)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:230)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:912)
	at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethodOnCurrentN(ScriptBytecodeAdapter.java:78)
	at DatabaseResource.<init>(DatabaseResource.groovy:5)
	at OfWadsServlet.service(OfWadsServlet.java:15)
	... 13 more

mysql でコネクション数を見てみる*1と、見事にリーク*2してました。

mysql> show status like 'Threads_connected';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_connected | 4     |
+-------------------+-------+
1 row in set (0.00 sec)

コネクションの最大数が5で、1回のアクセスで2回groovy.sql.Sql クラスを生成しているので、1回のアクセスあたり2つのコネクションがリークしていたわけです。ということで、リクエスト毎にコネクションのクローズをするとともに、Sql.getConnection() でコネクションを取得して使いまわすように修正しました。

groovy で groovy.sql.Sql クラスを使う場合でも、当然コネクションのクローズ(groovy.sql.Sql#close())はきちんとやらなくちゃですね。

油断してました (^^;

*1:show status like 'Threads_connected'

*2:現在の my.ini の設定は、"max_connections = 5", "max_user_connections = 5" を設定。