Подтвердить что ты не робот

Является ли C3P0 потокобезопасным?

Исключение прерывания (java.lang.InterruptedException) происходит, когда я пытаюсь выполнить некоторые простые операции чтения (SELECT), используя C3P0 в базе данных MySQL. Исключение возникает, когда я увеличиваю количество параллельных потоков более 100 (я пробовал с 5,10,20,60 и 100). Выполнение, которое я выполняю, прост как:

SELECT `Model.id` FROM `Model` LIMIT 100;

Мои соединения объединены из ComboPooledDataSource, который настроен с использованием следующих свойств (см. также Руководство C3P0):

c3p0.jdbcUrl=jdbc:mysql... 
c3p0.debugUnreturnedConnectionStackTraces=true
c3p0.maxIdleTime=5
c3p0.maxPoolSize=1000
c3p0.minPoolSize=5
c3p0.initialPoolSize=5
c3p0.acquireIncrement=3
c3p0.acquireRetryAttempts=50
c3p0.numHelperThreads=20
c3p0.checkoutTimeout=0
c3p0.testConnectionOnCheckin=true
c3p0.testConnectionOnCheckout=true
user=***
password=***

Сервер MySQL на машине, на которой я запускаю тесты, настроен на прием 1024 подключений, и выполняемые ими единичные тесты успешно выполняются (данные извлекаются из базы данных, как ожидалось). Однако в файле журнала C3P0 я обнаружил следующее предупреждение:

15:36:11,449  WARN BasicResourcePool:1876 - [email protected] -- Thread unexpectedly interrupted while performing an acquisition attempt.
java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask.run(BasicResourcePool.java:1805)
    at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:547)

Я хотел бы знать причину этого предупреждения и, во-вторых, его возможное влияние на надежность и стабильность программного обеспечения. Обратите внимание: после использования я закрываю результирующий набор, инструкцию SQL и соединение. Наконец, как только тест закончен, я закрываю пул, вызывая метод ComboPooledDataSource#close(). Что более странно (и похоже, чтобы выявить проблему синхронизации), заключается в том, что если я даю достаточно времени пулу, используя следующее...

Thread.sleep(10000); // wait for some time
datasource.close();

В журналах не появится никаких предупреждений! Думаете ли вы, что это поднимает проблему безопасности потоков для C3P0, или я делаю что-то не так?

Обновление 1:

Позвольте мне упомянуть, что удаление Thread.sleep(10000), помимо того, что уже упоминается, приводит к регистрации следующей информации в файле журнала MySQL:

110221 14:57:13 [Warning] Aborted connection 9762 to db: 'myDatabase' user: 'root'
host: 'localhost' (Got an error reading communication packets)

Можно пролить немного света...

Обновление 2:

Вот моя конфигурация сервера MySQL. Количество максимально разрешенных подключений по серверу установлено на 1024 (как я уже упоминал выше), что является достаточным для того, что я пытаюсь сделать.

[mysqld]
max_allowed_packet  = 64M
thread_concurrency      = 8
thread_cache_size       = 8
thread_stack        = 192K
query_cache_size = 0
query_cache_type = 0
max_connections = 1024
back_log = 50
innodb_thread_concurrency = 6
innodb_lock_wait_timeout = 120
log_warnings

Чтобы устранить любые сомнения, я проверил, что максимальное количество соединений правильно установлено:

show global variables where Variable_name='max_connections';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 1024  | 
+-----------------+-------+
1 row in set (0.00 sec)
4b9b3361

Ответ 1

Это предупреждение появляется из строки здесь. Кажется, что поток застрял, пытаясь подключиться.

Возможно, потому, что пул настроен на подключение большего количества подключений, чем тот, который настроен для вашего сервера mysql. Это, по-видимому, имеет смысл, поскольку по умолчанию max_connection составляет 100 (или 151 в зависимости от вашей версии mysql)

Итак, чтобы поток, пытающийся подключиться к соединению, включался в цикл sleep()/retry, пытаясь соединение - однако вы закрываете весь пул, пока он находится внутри этого цикла, - этот поток прерывается, поэтому все ресурсы могут быть восстановлены при закрытии пула.

До сих пор, кажется, никакого вреда не было, ваш код, вероятно, вернет соединения с пулом, когда вы закончите с ним, оставив их бездействующими для других, и все ваши запросы пройдут.

Ответ 2

Возможно, InterruptedException является нормальным, потому что некоторые потоки c3p0 ждут соединения, а когда вы вызываете close(), эти потоки прерываются. Хотя, согласно вашей настройке (100 клиентов, 1000 серверных подключений), такая необходимость ждать ресурса не так очевидна.
Если вам действительно интересно, самое надежное решение будет искать журналы c3p0, возможно, добавив еще несколько журналов и перекомпилировав...

Ответ 3

Я столкнулся с этой проблемой. Вот моя настройка для DataSource:

 [java:comp/env/jdbc/pooledDS] = [com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 2siwtu8o4m410i1l4tkxb|187c55c, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> null, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 2siwtu8o4m410i1l4tkxb|187c55c, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> null, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]]

и зафиксировано:

[java:comp/env/jdbc/pooledDS] = [com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, dataSourceName -> 2siwtu8o4m5kux117kgtx|13e754f, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> oracle.jdbc.driver.OracleDriver, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, identityToken -> 2siwtu8o4m5kux117kgtx|13e754f, idleConnectionTestPeriod -> 0, initialPoolSize -> 3, jdbcUrl -> jdbc:oracle:thin:@localhost:1521:oracle, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 15, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, numThreadsAwaitingCheckoutDefaultUser -> 0, preferredTestQuery -> null, properties -> {user=******, password=******}, propertyCycle -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, usesTraditionalReflectiveProxies -> false ]]

Значит, не все было правильно настроено. Более конкретно, когда я вызывал setDriverClass и setJdbcUrl для исправления нулевых значений, я исключил InterruptedException