Blog from September, 2014

Across and HikariCP

What is HikariCP?

HikariCP is an alternative lightweight connection pool, it's an alternative some of the well known connection pools:

HikariCP claims to be the fastest of them all, see the benchmarks on https://github.com/brettwooldridge/HikariCP

Usage in Across

For the Knooppunt project we switched the Across Datasource to use HikariCP, instead of using DBCP, this is the  existing code block for DBCP:

	@Bean
	@DependsOn("logbackConfigurer")
	public DataSource acrossDataSource() {
		BasicDataSource dataSource = new BasicDataSource();
		dataSource.setDriverClassName( environment.getRequiredProperty( "datasource.driver" ) );
		dataSource.setUrl( environment.getRequiredProperty( "datasource.url" ) );
		dataSource.setUsername( environment.getRequiredProperty( "datasource.username" ) );
		dataSource.setPassword( environment.getRequiredProperty( "datasource.password" ) );
		return dataSource;
	}

This the configuration for Hikari:

	@Bean
	@DependsOn("logbackConfigurer")
	public DataSource acrossDataSource() {
        HikariConfig config = new HikariConfig();
        config.setMaximumPoolSize(20);
        config.setDataSourceClassName( environment.getRequiredProperty( "datasource.classname" ) );
        String serverName = environment.getRequiredProperty( "datasource.servername" );
        if( StringUtils.isNotEmpty( serverName ) ) {
            // not required for HSQLDB
            config.addDataSourceProperty("serverName", serverName );
        }
        String port = environment.getRequiredProperty( "datasource.port" );
        if(  StringUtils.isNotEmpty( port ) ) {
            // not required for HSQLDB
            config.addDataSourceProperty("port", port );
        }
        config.addDataSourceProperty("databaseName", environment.getRequiredProperty( "datasource.database" ));
        config.addDataSourceProperty("user", environment.getRequiredProperty( "datasource.username" )  );
        config.addDataSourceProperty("password", environment.getRequiredProperty( "datasource.password" ));
        return new HikariDataSource( config);
	}

The properties are pretty self-explanatory, basically you need the the datasource class name, servername, port, databasename, dbuser and dbpassword passed to Hikari.

DataSourceClassName vs JDBCUrl

There are two ways of configuring HikariDatasource:

  • Via DataSourceClassName (recommended)
  • Via JDBC Url

The DataSourceClassName option requires a few more properties to be set, but is recommended because it directly instantiates the correct database driver.

The list of popular DataSourceClassName can be found on https://github.com/brettwooldridge/HikariCP, the most popular are listed here:

DatabaseDriverDataSource class
Apache DerbyDerbyorg.apache.derby.jdbc.ClientDataSource
HSQLDBHSQLDBorg.hsqldb.jdbc.JDBCDataSource
MariaDB & MySQLMariaDBorg.mariadb.jdbc.MySQLDataSource
MySQLConnector/Jcom.mysql.jdbc.jdbc2.optional.MysqlDataSource
MS SQL ServerMicrosoftcom.microsoft.sqlserver.jdbc.SQLServerDataSource
OracleOracleoracle.jdbc.pool.OracleDataSource

 

With JDBCurl, when using the following construct:

config.setJdbcUrl("jdbc:mysql://localhost:3306/mysqldb");

Hikari will have to wrap the Datasource into a DriverDataSource class, leading to more calls and cause a performance hit as can be seen in HikariPool.java

HikariPool.java
return new DriverDataSource(configuration.getJdbcUrl(), configuration.getDataSourceProperties(), username, password);

Hibernate 4.2 bug leading to premature close()

However, when switching the datasource we encountered the following error in the project:

2014-09-04 12:12:46,069 [] [RMI TCP Connection(3)-127.0.0.1] WARN  - Connection com.mysql.jdbc.JDBC4Connection@123dead (HikariPool-0) marked as broken because of SQLSTATE(08003), ErrorCode(0).
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after statement closed.

Looking this up led to several threads of which most important is https://hibernate.atlassian.net/browse/HHH-8853 which seems to be a Hibernate bug that was fixed in 4.3

To work around this issue we have created a fix in Across, which uses FixedBatchBuilderImpl and FixedNonBatchingBatch classes to fix this, only if the detected version is 4.2.

The workaround was pushed to https://bitbucket.org/beforeach/across-standard-modules/commits/d73294e8bb9424d04812ae1e6f25de883de6f1e6 for the across-hibernate module in 1.0.4-SNAPSHOT.

Note that Upgrading to Hibernate 4.3 might be an issue if you are using JBoss!

Hibernate 4.3 supports JPA 2.1 but is not compatible with JPA 2.0. The latest JBoss wildfly release does not support JPA 2.1.

Several threads have been started: