HIbernate - How to close multi-tenant connection pool?
Asked Answered
L

0

7

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

Lorinalorinda answered 13/11, 2015 at 17:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.