Hello!
I've been struggling for some time with a problem during load testing an app and it seems that for some strange reason a SessionFactory with an associated C3P0 datasource will not close properly, leaving several threads and a pool with open connections after each test. I wonder if anyone has come across this situation.
Even more specifically, this only happens if the C3P0 integration is done via a dataSource bean, but NOT if done via hibernate's c3p0 properties.
I managed to narrow the issue to this JUnit test (The dumpThreads method is not relevant to the problem, it's just a debugging aid)
This test uses only Hibernate or Spring+Hibernate.Code:package com.planetalia.webng.hibernate; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.AnnotationConfiguration; import org.hibernate.cfg.Configuration; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; public class ConnectionTest{ private static final int HIBERNATE = 1; private static final int SPRING= 2; private int mode = HIBERNATE; private void dumpThreads() { StringBuilder sb = new StringBuilder("THREAD COUNT IS "); sb.append(Thread.activeCount()); sb.append(": "); Thread[] liveThreads = new Thread[Thread.activeCount()]; long id = Thread.currentThread().getId(); Thread.enumerate(liveThreads); for (int i = 0; i < liveThreads.length; i++) { long tid = liveThreads[i].getId(); sb.append(tid); sb.append((tid==id)?"*(":"("); StackTraceElement[] stack = liveThreads[i].getStackTrace(); int lastElement = stack.length-1; if (lastElement >=0) { sb.append(stack[lastElement].getClassName()); sb.append("."); sb.append(stack[lastElement].getMethodName()); sb.append(")"); } sb.append(liveThreads[i].getState()); sb.append("\t"); } System.out.println(sb); } @Before public void start() throws Exception { SessionFactory sessionFactory = null; switch (mode) { case HIBERNATE: { Configuration configuration = new AnnotationConfiguration(); configuration.configure("hibernate.cfg.xml"); sessionFactory = configuration.buildSessionFactory(); break; } case SPRING: { ApplicationContext ctx = new FileSystemXmlApplicationContext("wiring.xml"); sessionFactory = (SessionFactory)ctx.getBean("sessionFactory"); break; } } Session session = sessionFactory.getCurrentSession(); Transaction tx = session.beginTransaction(); tx.commit(); sessionFactory.close(); dumpThreads(); } @Test public void test1() throws Exception {Thread.sleep(500);} @Test public void test2() throws Exception {Thread.sleep(500);} @Test public void test3() throws Exception {Thread.sleep(500);} @Test public void test4() throws Exception {Thread.sleep(500);} @Test public void test5() throws Exception {Thread.sleep(500);} @Test public void test6() throws Exception {Thread.sleep(500);} @Test public void test7() throws Exception {Thread.sleep(500);} @Test public void test8() throws Exception {Thread.sleep(500);} }
For the first mode, the following configuration file is used (hibernate.cfg.xml):
And for the second (Spring+Hibernate) mode, the following file is used (wiring.xml):Code:<hibernate-configuration> <session-factory> <property name='connection.driver_class'>com.mysql.jdbc.Driver</property> <property name='connection.url'>jdbc:mysql://localhost/test</property> <property name='connection.username'>test</property> <property name='connection.password'>test</property> <property name='dialect'>org.hibernate.dialect.MySQLInnoDBDialect</property> <property name='current_session_context_class'>thread</property> <property name='cache.provider_class'>org.hibernate.cache.NoCacheProvider</property> <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property> <property name="hibernate.c3p0.min_size">15</property> <property name="hibernate.c3p0.max_size">200</property> <property name="hibernate.c3p0.max_statements">100</property> </session-factory> </hibernate-configuration>
If the test is run in the SPRING mode, each consecutive test leaves behind 4 threads and a fully built (and connected) pool, regardless of the sessionFactory.close() method. According to the dump, the threads are basically waiting on some monitor. But this only happens if the C3P0 initialization is performed using <property name="dataSource" ref="dataSource"/>. If I remove that property and instead initialize C3P0 using the commented hibernate properties, it works properly.Code:<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost/test"/> <property name="user" value="test"/> <property name="password" value="test"/> <property name="minPoolSize" value="15"/> <property name="maxPoolSize" value="200"/> <property name="maxStatements" value="100"/> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="hibernateProperties"> <value> <!-- hibernate.connection.driver_class = com.mysql.jdbc.Driver hibernate.connection.url=jdbc:mysql://localhost/test hibernate.connection.username=test hibernate.connection.password=test hibernate.connection.provider_class = org.hibernate.connection.C3P0ConnectionProvider hibernate.c3p0.min_size=15 hibernate.c3p0.max_size=200 hibernate.c3p0.max_statements=100 --> hibernate.dialect = org.hibernate.dialect.MySQLInnoDBDialect hibernate.cache.provider_class = org.hibernate.cache.NoCacheProvider hibernate.show_sql = false hibernate.current_session_context_class=thread </value> </property> </bean>
If the test is run in the HIBERNATE mode, there are no "leftover" threads and/or pools. However, if in this mode I "forget" to call sessionFactory.close(), I get exactly the same behaviour as the SPRING mode WITH sessionFactory.close();
(Versions are: Hibernate: 3, C3P0: 0.9.1, Spring: 2.5.6.SEC01 (2009-04-22) )
Any ideas or similar problems? Thanks!


Reply With Quote
