Amazon がホストする Play アプリケーションで RDS Mysql を使用すると問題が発生します。
アプリケーションが応答しなくなることがあります。今日、jstack -F でスレッドのステータスを確認したところ、c3p0 接続のネイティブ コードに複数のスレッドがスタックしていました。
Thread 14060: (state = IN_NATIVE)
- java.net.SocketInputStream.socketRead0(java.io.FileDescriptor, byte[], int, int, int) @bci=0 (Compiled frame; information may be imprecise)
- java.net.SocketInputStream.read(byte[], int, int, int) @bci=79, line=150 (Compiled frame)
- java.net.SocketInputStream.read(byte[], int, int) @bci=11, line=121 (Compiled frame)
- com.mysql.jdbc.util.ReadAheadInputStream.fill(int) @bci=262, line=114 (Compiled frame)
- com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(byte[], int, int) @bci=176, line=161 (Compiled frame)
- com.mysql.jdbc.util.ReadAheadInputStream.read(byte[], int, int) @bci=48, line=189 (Compiled frame)
- com.mysql.jdbc.MysqlIO.readFully(java.io.InputStream, byte[], int, int) @bci=34, line=3036 (Compiled frame)
- com.mysql.jdbc.MysqlIO.reuseAndReadPacket(com.mysql.jdbc.Buffer, int) @bci=23, line=3489 (Compiled frame)
- com.mysql.jdbc.MysqlIO.reuseAndReadPacket(com.mysql.jdbc.Buffer) @bci=3, line=3478 (Compiled frame)
- com.mysql.jdbc.MysqlIO.checkErrorPacket(int) @bci=12, line=4019 (Compiled frame)
- com.mysql.jdbc.MysqlIO.sendCommand(int, java.lang.String, com.mysql.jdbc.Buffer, boolean, java.lang.String, int) @bci=428, line=2490 (Compiled frame)
- com.mysql.jdbc.MysqlIO.sqlQueryDirect(com.mysql.jdbc.StatementImpl, java.lang.String, java.lang.String, com.mysql.jdbc.Buffer, int, int, int, boolean, java.lang.String, com.mysql.jdbc.Field[]) @bci=559, line=2651 (Compiled frame)
- com.mysql.jdbc.ConnectionImpl.execSQL(com.mysql.jdbc.StatementImpl, java.lang.String, int, com.mysql.jdbc.Buffer, int, int, boolean, java.lang.String, com.mysql.jdbc.Field[], boolean) @bci=130, line=2677 (Compiled frame)
- com.mysql.jdbc.ConnectionImpl.execSQL(com.mysql.jdbc.StatementImpl, java.lang.String, int, com.mysql.jdbc.Buffer, int, int, boolean, java.lang.String, com.mysql.jdbc.Field[]) @bci=17, line=2627 (Compiled frame)
- com.mysql.jdbc.StatementImpl.executeQuery(java.lang.String) @bci=534, line=1556 (Compiled frame)
- com.mysql.jdbc.DatabaseMetaData$9.forEach(java.lang.Object) @bci=174, line=5013 (Compiled frame)
- com.mysql.jdbc.IterateBlock.doForAll() @bci=25, line=51 (Compiled frame)
- com.mysql.jdbc.DatabaseMetaData.getTables(java.lang.String, java.lang.String, java.lang.String, java.lang.String[]) @bci=280, line=4991 (Compiled frame)
- com.mchange.v2.c3p0.impl.DefaultConnectionTester.activeCheckConnectionNoQuery(java.sql.Connection, java.lang.Throwable[]) @bci=21, line=185 (Compiled frame)
- com.mchange.v2.c3p0.impl.DefaultConnectionTester.activeCheckConnection(java.sql.Connection, java.lang.String, java.lang.Throwable[]) @bci=7, line=62 (Compiled frame)
- com.mchange.v2.c3p0.AbstractConnectionTester.activeCheckConnection(java.sql.Connection) @bci=4, line=67 (Compiled frame)
- com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.testPooledConnection(java.lang.Object) @bci=122, line=368 (Compiled frame)
- com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.refurbishIdleResource(java.lang.Object) @bci=26, line=310 (Compiled frame)
- com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask.run() @bci=35, line=1999 (Interpreted frame)
- com.mchange.v2.async.ThreadPerTaskAsynchronousRunner$TaskThread.run() @bci=41, line=255 (Interpreted frame)
他の接続テストが設定されていない場合、DefaultConnectionTester.activeCheckConnectionNoQuery は DatabaseMetaData.getTables をチェックするようです。
C3p0 マニュアルには、適切な接続テストを構成するための手順があり、この回答には Play! しかし、私は疑問に思っています:
- これの背後にある問題を確認する方法はありますか?
- 例外として発生する前に発生した標準の「Mysql接続が8時間後に強制終了されました」
- これが完了してから、c3p0/Play/Hibernate を使用したデフォルトの接続テストが行われているようですか?
- デフォルトでは c3p0 は接続テストを行わないという予感があります
- デフォルト値 idleConnectionTestPeriod = 0、testConnectionOnCheckin = false、testConnectionOnCheckout = false
- デフォルトでは c3p0 は接続テストを行わないという予感があります
- 接続テスト クエリのタイムアウトを設定する方法はありますか?
- たとえば、賢明なpreferredTestQueryを構成したとしても、そのクエリも現在のgetTables()呼び出しとしてハングするリスクはありますか?
- 他に考慮すべきことはありますか?
automaticTestTable を使用した UPDATE (解決策ではありませんでした)
c3p0.automaticTestTable を設定して、より適切な接続テストを行いましたが、スレッドのスタック トレースを少し変更しただけで、問題が解決されたようには見えませんでした。
同様のケースで、スレッド スタックは次のようになります。
"Task-Thread-for-com.mchange.v2.async.ThreadPerTaskAsynchronousRunner@6064cd08" daemon prio=10 tid=0x00007f53d403a000 nid=0x63dd runnable [0x00007f53dce4a000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:150)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
at com.mysql.jdbc.util.ReadAheadInputStream.fill(ReadAheadInputStream.java:114)
at com.mysql.jdbc.util.ReadAheadInputStream.readFromUnderlyingStreamIfNecessary(ReadAheadInputStream.java:161)
at com.mysql.jdbc.util.ReadAheadInputStream.read(ReadAheadInputStream.java:189)
- locked <0x00000000dbecd0d8> (a com.mysql.jdbc.util.ReadAheadInputStream)
at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:3036)
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3489)
at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3478)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4019)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2490)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2651)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2677)
- locked <0x00000000dbec1a70> (a com.mysql.jdbc.JDBC4Connection)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2627)
at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1556)
- locked <0x00000000dbec1a70> (a com.mysql.jdbc.JDBC4Connection)
- locked <0x00000000d1330078> (a com.mysql.jdbc.StatementImpl)
at com.mchange.v2.c3p0.impl.DefaultConnectionTester.activeCheckConnection(DefaultConnectionTester.java:73)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.testPooledConnection(C3P0PooledConnectionPool.java:374)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.refurbishIdleResource(C3P0PooledConnectionPool.java:310)
at com.mchange.v2.resourcepool.BasicResourcePool$AsyncTestIdleResourceTask.run(BasicResourcePool.java:1999)
at com.mchange.v2.async.ThreadPerTaskAsynchronousRunner$TaskThread.run(ThreadPerTaskAsynchronousRunner.java:255)
Play スレッドは DB 接続の取得を待機していました:
"play-thread-3" prio=10 tid=0x00007f53c400b000 nid=0x5b3e in Object.wait() [0x00007f53ddd57000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000db9f38f8> (a com.mchange.v2.resourcepool.BasicResourcePool)
at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:579)
- locked <0x00000000db9f38f8> (a com.mchange.v2.resourcepool.BasicResourcePool)
at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:584)
... (näitä vaihtelevat määrät)
at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:584)
- locked <0x00000000db9f38f8> (a com.mchange.v2.resourcepool.BasicResourcePool)
at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:584)
- locked <0x00000000db9f38f8> (a com.mchange.v2.resourcepool.BasicResourcePool)
at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:477)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:525)
at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:128)
at org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider.getConnection(InjectedDataSourceConnectionProvider.java:71)
at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:446)
at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:167)
at org.hibernate.jdbc.JDBCContext.connection(JDBCContext.java:160)
at org.hibernate.transaction.JDBCTransaction.begin(JDBCTransaction.java:81)
at org.hibernate.impl.SessionImpl.beginTransaction(SessionImpl.java:1473)
at org.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:60)
at play.db.jpa.JPAPlugin.startTx(JPAPlugin.java:377)
at play.db.jpa.JPAPlugin.beforeInvocation(JPAPlugin.java:345)
at play.plugins.PluginCollection.beforeInvocation(PluginCollection.java:473)
at play.Invoker$Invocation.before(Invoker.java:217)
at play.Invoker$Invocation.run(Invoker.java:277)
at play.server.PlayHandler$NettyInvocation.run(PlayHandler.java:229)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
at java.lang.Thread.run(Thread.java:722)
いくつかのメモ:
- 可能性は低いと思いますが、(少なくとも理論上は) スタック ダンプが読み取られると同時に多くの接続がテストされる可能性があります。
- 別の質問では、データのデッドロックまたはハードウェア/ネットワークの問題がこの種の動作を引き起こす可能性があると言及されました
.
- 少なくとも、進行中のスキーマ変更はありません。
br, トウコ
自分自身に答える:
- 接続がハングする理由が見つかりません:(
- RDS には MySQL の error.log がありますが、残念ながら何もありません
- Play には、c3p0 ComboPooledDataSource ( DBPlugin.onApplicationStart()内)
のデフォルト設定がいくつかあります。
- idleTimeExcessConnections : application.conf db.pool.maxIdleTimeExcessConnections から (デフォルト 0)
- checkoutTimeout : application.conf db.pool.timeout から (デフォルトは 5000)
- アイドル接続テスト期間: 10
- setTestConnectionOnCheckin : true
- さらに、conf/c3p0.properties を使用してカスタム プロパティを設定できます。
- 接続テストのタイムアウトを設定する方法が見つかりませんでした (簡単なDefaultConnectionTesterソース コード ルックアップでは、サポートされていない可能性があります)。
c3p0.automaticTestTable の設定を進めて、後で問題がないか確認します。
更新: 残念ながら、まだ問題を解決できていません:( また、automaticTestTable を使用すると、接続テストがスタックしたようです java.net.SocketInputStream.socketRead0(Native Method)
自分自身にもう一度答える:
c3p0:s maxAdministrativeTaskTimeを使用して、この「接続テスト クエリのタイムアウトを設定する」アプローチを試すことになりました。これはちょっとしたハックなので、これが最終的な解決策になるかどうかを確認する必要があります (機能する場合)。
これまでのところ問題はありません。これがどのように機能するかを見ていきます。ログに c3p0 エラー/スタック トレースもありません。この中断がログに表示されると思いますか? タイムアウトがまだ発生していないか、c3p0 ログが表示されるようにロギング構成を調整する必要があります。
更新 (2013-04-04) : この後、問題は (おそらく) それほど頻繁には発生していませんが、時々発生しています :(