I'm having troubles with hibernate not closing the (hikari) connection pool in a multi-tenant setup. Here is the hibernate session-factory config:
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.PostgreSQL82Dialect</property>
<property name="hibernate.multiTenancy">SCHEMA</property>
<property name="hibernate.multi_tenant_connection_provider">foundation.hibernate.TaxonomyMultiTenantConnectionProvider</property>
<property name="hibernate.jdbc.batch_size">100</property>
<property name="hibernate.order_inserts">true</property>
<property name="hibernate.hikari.dataSourceClassName">org.postgresql.ds.PGSimpleDataSource</property>
<property name="hibernate.hikari.minimumIdle">50</property>
<property name="hibernate.hikari.connectionTestQuery">SELECT 1</property>
<property name="hibernate.hikari.maximumPoolSize">200</property>
<property name="hibernate.hikari.idleTimeout">28000000</property>
<property name="hibernate.hikari.maxLifetime">28000000</property>
<property name="hibernate.hikari.connectionTimeout">10000</property>
<property name="hibernate.hikari.dataSource.applicationName">Taxonomy Workbench</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<mapping class="dal.models.DbProduct"/>
</session-factory>
The implementation of TaxonomyMultiTenantConnectionProvider is as follows:
package foundation.hibernate;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import foundation.hibernate.TenantDoesNotExistException.TenantDoesNotExistException;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.service.spi.ServiceRegistryAwareService;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
public class TaxonomyMultiTenantConnectionProvider implements MultiTenantConnectionProvider, ServiceRegistryAwareService, AutoCloseable {
private HikariDataSource dataSource;
@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
Properties hikariSettings = new Properties();
String hikariPrefix = "hibernate.hikari.";
serviceRegistry.getService(ConfigurationService.class).getSettings().forEach((key, value) -> {
if (key instanceof String) {
String skey = (String) key;
if (skey.toLowerCase().startsWith(hikariPrefix)) {
hikariSettings.put(skey.substring(hikariPrefix.length()), value);
}
}
});
dataSource = new HikariDataSource(new HikariConfig(hikariSettings));
}
@Override
public Connection getAnyConnection() throws SQLException {
System.out.println("getAnyConnection");
return dataSource.getConnection();
}
@Override
public void releaseAnyConnection(Connection connection) throws SQLException {
System.out.println("releaseAnyConnection");
dataSource.evictConnection(connection);
}
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
//Acquire connection
Connection connection = getAnyConnection();
//Check if schema exists
ResultSet rs = connection.createStatement().executeQuery(ConnectionSQL.schemaExists(tenantIdentifier));
boolean found = false;
while (rs.next()) {
if (rs.getInt(1) == 1) {
found = true;
}
}
rs.close();
if (!found) {
throw new TenantDoesNotExistException(tenantIdentifier);
}
//Change schema
try {
connection.createStatement().executeUpdate(ConnectionSQL.selectSchema(tenantIdentifier));
} catch (Exception e) {
throw new RuntimeException("Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", e);
}
return connection;
}
@Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
System.out.println("releaseConnection");
dataSource.evictConnection(connection);
}
@Override
public boolean supportsAggressiveRelease() {
System.out.println("supportsAggressiveRelease");
return false;
}
@Override
public boolean isUnwrappableAs(Class unwrapType) {
System.out.println("isUnwrappableAs");
return false;
}
@Override
public <T> T unwrap(Class<T> unwrapType) {
System.out.println("unwrap");
return null;
}
@Override
public void close(){
System.out.println("close");
dataSource.close();
}
}
The pooling and working with connections is fine. However, calling sessionFactory.close() will not close the connection pool.
I found this answer. But in my case, getConnectionProvider() returns null. The code:
if (sessionFactory != null) {
Logger.debug("Closing SessionFactory.");
sessionFactory.close();
if(sessionFactory instanceof SessionFactoryImpl){
SessionFactoryImpl impl = (SessionFactoryImpl) sessionFactory;
Logger.debug("Closing database connections.");
ConnectionProvider conn = impl.getConnectionProvider();
if(conn instanceof TaxonomyMultiTenantConnectionProvider){
((TaxonomyMultiTenantConnectionProvider) conn).close();
} else {
throw new RuntimeException("Unexpected ConnectionProvider");
}
}
}
Any ideas on what I'm doing wrong?
Thanks in advance!
edit
BTW: My hibernate version is 4.3.6-Final