Thursday, January 3, 2013

MySQL JPA EntityManager Timeout


Few weeks back, I had a MySQL databases connectivity issues in the web application I was working on. The issue was there with daily first login to the application. Initially we thought that it was the LDAP (AD or Active Directory) connectivity issue as it appeared after the authentication with LDAP.

Problem Description: Daily morning first login attempt to my web application built on Spring, JPA and MySQL technologies fails.


Root Cause: After a given period of time (8 hours), MySQL database server automatically closes unused connections. The issues comes because JPA uses the same C3P0 configuration used by Hibernate which does not automatically checks or refreshes it connection pool when a connection is closed by database server.

Solution: I did a lot of google search to find out the solution. Most of the resources on internet suggested configuration changes in Hibernate configuration file (hibernate.cfg.xml). Many sites on Internet suggested adding different properties of c3p0 pool. Mostly the error occurs when we forget to configure and assign the value to one or more important property in the c3p0 connection pool, and the default value is assumed by the framework.

My applicationContext.xml file in the Spring/JPA/MySQL based application (after adding the required property: idleConnectionTestPeriod) has following bean definitions:

<!-- The JPA Transaction Manager -->
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>


<!-- The global EntityManagerFactory Instance -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
lazy-init="true">
<property name="dataSource" ref="dataSource" />
<property name="persistenceXmlLocation" value="classpath:META-INF/ec-persistence.xml" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<!-- <property name="generateDdl" value="false" />-->
<property name="showSql" value="true" />
</bean>
</property>
</bean>


<!-- The global application DataSource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />

<!-- C3P0 specific DataSource properties -->
<property name="initialPoolSize" value="${c3p0.initialPoolSize}" />
<property name="minPoolSize" value="${c3p0.minPoolSize}" />
<property name="maxPoolSize" value="${c3p0.maxPoolSize}" />
<property name="acquireIncrement" value="${c3p0.acquireIncrement}" />
<property name="idleConnectionTestPeriod" value="$ {c3p0.idle_test_period}" />
</bean>


<!-- Configurer that replaces ${...} placeholders with values from a properties file -->
<!-- (in this case, JDBC-related settings for the JPA EntityManager and
DataSource definitions below) -->
<context:property-placeholder location="/WEB-INF/config/jdbc.properties" />


Jdbc.properties

#MySQL Database Settings
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/erilearning?autoReconnect=true
jdbc.username=erilearning
jdbc.password=erilearning

#Connection Pooling Settings
c3p0.initialPoolSize=5
c3p0.minPoolSize=5
c3p0.maxPoolSize=50
c3p0.acquireIncrement=5

# add following property. Value in seconds
c3p0.idle_test_period=3600



To identify all the properties available in the com.mchange.v2.c3p0.ComboPooledDataSource class, download the source jar (c3p0-0.9.1-sources.jar) of the c3p0-0.9.1.jar

One of the important property of the c3p0 is idleConnectionTestPeriod. After adding this property value less that the corresponding value at server end (8 hours), we can test the closed connections and the timed out connections by the connection pool at application end with the corresponding reference on database server side.


Testing: To test the changes applied in above step, we need to wait for at least 8 hours, which would be a long time. We can reduce this testing time by changing the connection time out in the MySQL server, restarting MySQL server and restarting application server. Add the following line to MySQL configuration file:

wait_timeout=120

MySQL Configuration File Name on windows: my.ini
MySQL Configuration File Name on Linux: /etc/mysql/my.cnf

Linux users must use the sudo command to edit the file as given below:

sudo vi /etc/mysql/my.cnf

Once you're done with your testing, you can remove the property and it will default back to 8 hours.


References:



No comments: