Проблема:
Конфигурация c3p0 следующая:
При нагрузочном тестировании веб-приложения, использующего пул соединений c3p0, при высокой нагрузке (порядка 170 запросов в секунду) иногда в логи валится исключение следующего вида:
com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector -- APPARENT DEADLOCK!!!приводящие, в конечном итоге, к SQLExceptio`у в приложении.
Конфигурация c3p0 следующая:
<Resource name="jdbc/myDb" auth="Container"
type="com.mchange.v2.c3p0.ComboPooledDataSource"
factory="org.apache.naming.factory.BeanFactory"
user="xxx" password="yyy" driverClass="oracle.jdbc.driver.OracleDriver"
jdbcUrl="jdbc:oracle:thin:@//myoraserver:1521/zzz"
maxPoolSize="50"
maxIdleTime="7200"
minPoolSize="5"
testConnectionOnCheckout="true"
idleConnectionTestPeriod="300"
maxIdleTimeExcessConnections="120"/>
Решение:
Здесь в одном из постов предполагается, что такая проблема вызвана одновременным закрытием закэшированного PreparedStatement и используемого им соединения (как такое может случится, я не понял, но пост писал человек, видимо, видевший исходники c3p0, так что я поверил ему на слово), и рекомендуется два выхода:
- Запретить кэширование PreparedStatement`ов вообще установкой maxStatements=0
- Установить maxStatements в 0, а maxStatementsPerConnection равным (или чуть большим) чем число различных PreparedStatement`ов, используемых приложением, чтобы уменьшить вероятность возникновения данной ситуации.
Почему 2 вариант поможет, я не совсем понял, но попробовал его у себя, т.к. помимо борьбы с вышеуказанной проблемой, такая настройка позволяет повысить эффективность кэширования (негативно, правда, сказываясь на использовании памяти) за счет того, что все используемые приложением
PreparedStatement `ы будут браться из кэша. Не помогло - исключения всё равно валятся, так что пришлось прибегнуть к первому варианту. Но и он оказался безуспешным - валились те же исключения.
После долгих поисков был найден следующий вариант решения этой проблемы: если установить numHelperThreads равным maxPoolSize, APPARENT DEADLOCK`и проходят. Не самый красивый вариант в плане требовательности к ресурсам, но работает.
Второй вариант, на котором я, пожалуй, и остановлюсь - это установка maxAdministrativeTaskTime в некоторое достаточно большое значение, за которое предполагается, что любой запрос к БД выполнится (например, 600 сек). Тогда пока это время не истечет, c3p0 не будет расценивать ситуацию как deadlock. Документация утверждает, что установка этого значения в 0 приводит к тому, что "зависший" поток никогда не будет прерван, однако реально это нет так: c3p0 достаточно быстро ругается на deadlock, а через 60 секунд вызывает interrupt() потока.
Комментариев нет:
Отправить комментарий