barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)

среда, 11 апреля 2012 г.

c3p0: com.mchange.v2.async.ThreadPoolAsynchronousRunner$DeadlockDetector -- APPARENT DEADLOCK!!!

Проблема:

При нагрузочном тестировании веб-приложения, использующего пул соединений 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, так что я поверил ему на слово), и рекомендуется два выхода:
  1. Запретить кэширование  PreparedStatement`ов вообще установкой maxStatements=0
  2. Установить maxStatements в 0, а maxStatementsPerConnection равным (или чуть большим) чем число различных  PreparedStatement`ов, используемых приложением, чтобы уменьшить вероятность возникновения данной ситуации. 
Почему 2 вариант поможет, я не совсем понял, но попробовал его у себя, т.к. помимо борьбы с вышеуказанной проблемой, такая настройка позволяет повысить эффективность кэширования (негативно, правда, сказываясь на использовании памяти) за счет того, что все используемые приложением  PreparedStatement `ы будут браться из кэша. Не помогло - исключения всё равно валятся, так что пришлось прибегнуть к первому варианту. Но и он оказался безуспешным - валились те же исключения.
После долгих поисков был найден следующий вариант решения этой проблемы: если установить numHelperThreads равным maxPoolSize, APPARENT DEADLOCK`и проходят. Не самый красивый вариант в плане требовательности к ресурсам, но работает.
Второй вариант, на котором я, пожалуй, и остановлюсь - это установка maxAdministrativeTaskTime в некоторое достаточно большое значение, за которое предполагается, что любой запрос к БД выполнится (например, 600 сек). Тогда пока это время не истечет, c3p0 не будет расценивать ситуацию как deadlock. Документация утверждает, что установка этого значения в 0 приводит к тому, что "зависший" поток никогда не будет прерван, однако реально это нет так: c3p0 достаточно быстро ругается на deadlock, а через 60 секунд вызывает interrupt() потока.

Комментариев нет:

Отправить комментарий