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())はきちんとやらなくちゃですね。
油断してました (^^;