PostgreSQL + Hibernate + C3P0 = FATAL: извините, уже слишком много клиентов

У меня есть следующий код

    Configuration config = new Configuration().configure();
    config.buildMappings();
    serviceRegistry = new ServiceRegistryBuilder().applySettings(config.getProperties()).buildServiceRegistry(); 
    SessionFactory factory = config.buildSessionFactory(serviceRegistry);
    Session hibernateSession = factory.openSession();
    Transaction tx = hibernateSession.beginTransaction();
    ObjectType ot = (ObjectType)hibernateSession.merge(someObj);
    tx.commit();
    return ot;

hibernate.cfg.xml содержит:

<session-factory>

    <property name="connection.url">jdbc:postgresql://127.0.0.1:5432/dbase</property>
    <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
    <property name="connection.driver_class">org.postgresql.Driver</property>

    <property name="connection.username">username</property>
    <property name="connection.password">password</property>

    <property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>        
    <property name="hibernate.c3p0.acquire_increment">1</property>
    <property name="hibernate.c3p0.min_size">5</property>
    <property name="hibernate.c3p0.max_size">20</property>
    <property name="hibernate.c3p0.max_statements">50</property>
    <property name="hibernate.c3p0.timeout">300</property>
    <property name="hibernate.c3p0.idle_test_period">3000</property>
    <property name="hibernate.c3p0.acquireRetryAttempts">1</property>
    <property name="hibernate.c3p0.acquireRetryDelay">250</property>

    <property name="hibernate.show_sql">true</property>
    <property name="hibernate.use_sql_comments">true</property>

    <property name="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>
    <property name="hibernate.current_session_context_class">thread</property>

    <mapping class="...." />

</session-factory>

After a few seconds and some successful inserts, the following exception appears:

org.postgresql.util.PSQLException: FATAL: sorry, too many clients already
    at org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication(ConnectionFactoryImpl.java:291)
    at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:108)
    at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:66)
    at org.postgresql.jdbc2.AbstractJdbc2Connection.<init>(AbstractJdbc2Connection.java:125)
    at org.postgresql.jdbc3.AbstractJdbc3Connection.<init>(AbstractJdbc3Connection.java:30)
    at org.postgresql.jdbc3g.AbstractJdbc3gConnection.<init>(AbstractJdbc3gConnection.java:22)
    at org.postgresql.jdbc4.AbstractJdbc4Connection.<init>(AbstractJdbc4Connection.java:30)
    at org.postgresql.jdbc4.Jdbc4Connection.<init>(Jdbc4Connection.java:24)
    at org.postgresql.Driver.makeConnection(Driver.java:393)
    at org.postgresql.Driver.connect(Driver.java:267)
    at com.mchange.v2.c3p0.DriverManagerDataSource.getConnection(DriverManagerDataSource.java:135)
    at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:182)
    at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:171)
    at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.acquireResource(C3P0PooledConnectionPool.java:137)
    at com.mchange.v2.resourcepool.BasicResourcePool.doAcquire(BasicResourcePool.java:1014)
    at com.mchange.v2.resourcepool.BasicResourcePool.access$800(BasicResourcePool.java:32)
    at com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask.run(BasicResourcePool.java:1810)
    at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:547)
12:24:19.151 [          Thread-160] WARN                   internal.JdbcServicesImpl - HHH000342: Could not obtain connection to query metadata : Connections could not be acquired from the underlying database!
12:24:19.151 [          Thread-160] INFO                             dialect.Dialect - HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
12:24:19.151 [          Thread-160] INFO                  internal.LobCreatorBuilder - HHH000422: Disabling contextual LOB creation as connection was null
12:24:19.151 [          Thread-160] INFO        internal.TransactionFactoryInitiator - HHH000268: Transaction strategy: org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory
12:24:19.151 [          Thread-160] INFO               ast.ASTQueryTranslatorFactory - HHH000397: Using ASTQueryTranslatorFactory
12:24:19.151 [          Thread-160] INFO                        hbm2ddl.SchemaUpdate - HHH000228: Running hbm2ddl schema update
12:24:19.151 [          Thread-160] INFO                        hbm2ddl.SchemaUpdate - HHH000102: Fetching database metadata
12:24:19.211 [Runner$PoolThread-#0] WARN              resourcepool.BasicResourcePool - com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask@ee4084 -- Acquisition Attempt Failed!!! Clearing pending acquires. While trying to acquire a needed new resource, we failed to succeed more than the maximum number of allowed acquisition attempts (1). Last acquisition attempt exception: 
org.postgresql.util.PSQLException: FATAL: sorry, too many clients already
    at org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication(ConnectionFactoryImpl.java:291)

Кажется, что спящий режим не реализует соединение. Но hibernateSession.close() вызывает исключение Session is closed, потому что вызывается tx.commit().


person LancerX    schedule 16.10.2012    source источник


Ответы (3)


Я не совсем понимаю, что здесь происходит, но я бы рекомендовал вам не устанавливать для hibernate.c3p0.acquireRetryAttempts значение 1. Во-первых, это делает вашу следующую настройку, повторные попытки, но если есть только одна попытка (хорошо, имя параметра вводит в заблуждение, оно устанавливает общее количество попыток), повторных попыток нет. Эффект ваших настроек заключается в том, что пул просто пытается получить соединение всякий раз, когда приходит клиент, а затем немедленно выдает исключение клиентам, если это не удается. Это вовсе не ограничивает количество подключений, которые пул будет пытаться получить (если вы не установите для breakOnAcquireFailure значение true, и в этом случае с вашими настройками любая неудача в получении подключения приведет к аннулированию всего пула).

Я разделяю озабоченность Солы по поводу отсутствия надежной очистки ресурсов. Если в ваших настройках commit() означает close() (и вам не разрешено явно вызывать close? это кажется плохим), то именно коммит должен быть в блоке finally (но коммит в блоке finally тоже кажется плохим , иногда вы не хотите фиксировать). Какой бы ни была проблема с закрытием/фиксацией, с кодом, который у вас есть, случайные исключения между openSession и коммитом приведут к утечкам соединения.

Но это не должно быть причиной вашей проблемы со слишком многими открытыми соединениями. Если вы утечете соединения, вы обнаружите, что пул соединений в конечном итоге зависает (поскольку maxPoolSize соединения изымаются навсегда из-за утечек). У вас будет только 25 открытых подключений. Происходит что-то еще. Попробуйте просмотреть свои журналы. Инициализируется ли как-то более одного пула соединений? (c3p0 сбрасывает информацию о конфигурации на уровне INFO при инициализации пула, поэтому, если открывается несколько пулов, вы должны увидеть несколько сообщений. В качестве альтернативы вы можете проверить работающие пулы c3p0 через JMX, чтобы узнать, было ли/почему открыто более 25 соединений. )

Удачи!

person Steve Waldman    schedule 16.10.2012
comment
Ты прав. c3p0 инициализирует пул 41 раз и показывает сообщение Initializing c3p0 pool... com.mchange.v2.c3p0.PoolBackedDataSource@f3dc5b64 [ connectionPoolDataSource -> com.mchange.v2.c3p0.WrapperConnectionPoolDataSource@a959e809 [ acquireIncrement -> 1, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 0, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.... Эта версия сокращена, потому что она слишком длинная. Что может быть причиной? - person LancerX; 17.10.2012
comment
Ну, c3p0 не делает это автономно. Я предполагаю, что либо что-то в вашей настройке вызывает создание экземпляров многих источников данных c3p0, либо вы попадаете в пул с множеством разных пар пользователь/пароль. (c3p0 создает отдельный пул для каждой запрошенной пары пользователь/пароль.) - person Steve Waldman; 17.10.2012
comment
Я ожидал такого ответа :) Я должен найти причину. Точка для вас и спасибо! - person LancerX; 17.10.2012

Я нашел причину, почему c3p0 вел себя таким образом. Проблема была довольно тривиальной... Эта часть кода:

Configuration config = new Configuration().configure();
config.buildMappings();
serviceRegistry = new ServiceRegistryBuilder().applySettings(config.getProperties()).buildServiceRegistry(); 
SessionFactory factory = config.buildSessionFactory(serviceRegistry);

исполнялся неоднократно. Спасибо, Стив, за подсказку.

person LancerX    schedule 17.10.2012

Я предлагаю вам использовать блок try-catch-finally ,

в finally просьба закрыть сеанс

i.e

try {           
        tx.commit();
    } catch (HibernateException e) {
        handleException(e);
    } finally {
        hibernateSession.close();
    }

а также,
свойство max_connections в postgresql.conf по умолчанию равно 100. Увеличьте его, если вам нужно.

person solaimuruganv    schedule 16.10.2012
comment
Как я писал в своем вопросе, вызов hibernateSession.close() вызывает Exception in thread "Thread-18" org.hibernate.SessionException: Session is closed!. tx.commit() уже закрыл сеанс. Увеличение max_connections до 200 (и перезапуск) ничего не меняет :( Я также изменил <property name="connection.provider_class">org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider</property> с устаревшего org.hibernate.connection.C3P0ConnectionProvider. - person LancerX; 16.10.2012