Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
319 views
in Technique[技术] by (71.8m points)

java - Spring @Transactional not returning connection to Hikari pool

I have a REST based application server built on embedded jetty, spring boot, and hibernate. It uses an MS SQL backend database.

My @Transactional methods work but do not appear to close the db connection eventually resulting in no available hikari pool members:

java.sql.SQLTransientConnectionException: HikariPoolProduct - Connection is not available, request timed out after 30003ms.

Shortly after the above I will see the following hikari stats:

10:29:24.774 [HikariPoolProduct housekeeper] DEBUG HikariPoolProduct - Pool stats (total=50, active=49, idle=1, waiting=0)
10:29:24.774 [HikariPoolProduct housekeeper] DEBUG HikariPoolProduct - Fill pool skipped, pool is at sufficient level.

I would expect active to be 1 or 0.

In my test case I have my hikari pool size set to 50. When I repeatedly have my client call the server CertificatePolicyEntityResource.get() via REST it works fine until it tries #51 and then it fails as indicated above.

I'm using @Transactional methods and I'm pretty sure the wrapper/proxy is suppose to free the hikari pool member (via a db virtual/proxy close) before returning. This doesn't seem to be happening.

My code call tree:

client -> [REST] -> CertificatePolicyEntityResource.get() ->
CertificatePolicyEntityServletAdapter.get() ->
DomainRegistryModelProxy.certificatePolicyService() // Use spring ApplicationContext to retrieve CertificatePolicyServiceRepositoryImpl
CertificatePolicyServiceRepositoryImpl.size() and .allCertificatePolicies() // These are @Transactional

Here is my service class CertificatePolicyServiceRepositoryImpl which has the @Transactional methods:

package cmb.domain.model.certpolicy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Accessors( fluent = true )
public class CertificatePolicyServiceRepositoryImpl implements CertificatePolicyService {
        @Getter
        private CertificatePolicyRepository             certificatePolicyRepository;

        public CertificatePolicyServiceRepositoryImpl(CertificatePolicyRepository certificatePolicyRepository) {
                this.certificatePolicyRepository = certificatePolicyRepository;
        }

        @Override
        @Transactional
        public CertificatePolicy certificatePolicyOfId(String id) {
                return certificatePolicyRepository().certificatePolicyOfId(id);
        }

        @Override
        @Transactional
        public List<CertificatePolicy> allCertificatePolicies() {
                return certificatePolicyRepository().allCertificatePolicies();
        }

    @Override
        @Transactional
        public List<CertificatePolicy> allActiveCertificatePolicies() {
                return certificatePolicyRepository().allActiveCertificatePolicies();
        }

        @Override
        @Transactional(readOnly = true)
        public int size() {
                return certificatePolicyRepository().size();
        }

}

CertificatePolicyEntityResource.java:

package cmb.cabridge.port.servlet.resource;

import javax.ws.rs.BeanParam;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
    
@Path(CaBridgeCommandValues.RESOURCE_CERTIFICATEPOLICYENTITY)
public class CertificatePolicyEntityResource extends AbstractCaBridgeResource {
        @BeanParam
        private NetworkRequestContext                                   requestContext;

        @GET
        @Path(CaBridgeCommandValues.SUBRESOURCE_CERTIFICATEPOLICYENTITY_GET + "/{id}")
        public CertificatePolicyEntityResponse get(@PathParam("id") String idList,
                        @QueryParam(CertificatePolicyRequestFlags.FLAG_MINIENTITY) boolean miniEntityEnabled) {
                final String function = "Get by ID";
                ResponseHandler handler = createResponseHandler(m, function);

                try {
                        processRequest(requestContext,
                                        CaBridgeCommand.getInstance().create(CaBridgeCommandValues.CertificatePolicyEntityGet));
                        CertificatePolicyEntityResponse response = new CertificatePolicyEntityServletAdapter(caBridgeSessionContext())
                                        .get(idList, miniEntityEnabled);

                        handler.succeeded(response);
                        return response;
                } catch (Throwable e) {
                        CertificatePolicyEntityResponse response = new CertificatePolicyEntityResponse();
                        handler.failed(e, response);
                        return response;
                }
        }

}

Adapter:

package cmb.cabridge.port.servlet.adapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
    
@Component
public class CertificatePolicyEntityServletAdapter extends AbstractCaBridgeCommonServletAdapter {

        private CertificatePolicyService certificatePolicyService() {
            /*
             * Retrieve bean using Spring ApplicationContext
             */
                return DomainRegistryModelProxy.certificatePolicyService();
        }

        public CertificatePolicyEntityResponse get(String searchValue, boolean miniEntityEnabled) {
                List<CertificatePolicyEntity> results = new ArrayList<>();
                entityConverter().setMiniEntityEnabled(miniEntityEnabled);

                if (searchValue == null || searchValue.equalsIgnoreCase(GlobalConstantStandard.ALL)) {
                        if (certificatePolicyService().size() > 0)
                                certificatePolicyService().allCertificatePolicies().stream().forEach(
                                                item -> results.add(entityConverter.toEntity(item))
                                                                );
                } else {
                        List<String> ids = StringTool.splitAsList(searchValue, ",");
                        for (String id : ids) {
                                CertificatePolicy item = certificatePolicyService()
                                                                                        .certificatePolicyOfIdOrName(id);
                                if (item != null)
                                        results.add(entityConverter.toEntity(item));
                        }
                }

                CertificatePolicyEntityResponse response = new CertificatePolicyEntityResponse();
                if (results.size() == 0) {
                        String message = "No " + what + " found for ID/Name(s) " + searchValue;
                        log.info("%s", message);
                        response.setMessage(message);
                        response.setResult(Result.FAILED);
                        response.setFailureType(FailureType.NOTFOUND);
                } else {
                        response.setEntities( results );
                        response.setResult(Result.SUCCEEDED);
                }

                return response;
        }
}

Datasource class:

package cmb.cabridge.infrastructure.persistence.hibernate;

import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import cmb.common.infrastructure.DatabaseConfigurationTool;
import cmb.domain.model.CmbDomainModelMarker;
import cmb.product.domain.model.CmbProductDomainModelMarker;

import org.hibernate.SessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.orm.hibernate5.HibernateTransactionManager;
import org.springframework.orm.hibernate5.LocalSessionFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.Properties;

/**
 * CAB Hibernate Product Database Configuration
 */
@Configuration
@EnableJpaRepositories(
    entityManagerFactoryRef = HibernateProductDatabaseConfiguration.PRODUCT_ENTITY_MANAGER,
    transactionManagerRef = HibernateProductDatabaseConfiguration.PRODUCT_TX_MANAGER
)
@EnableTransactionManagement
public class HibernateProductDatabaseConfiguration {
        private Trace                                                                   log = TraceFactory.create(this);
        /*
         * Packages that Spring should scan to find @Entity classes
         */
        private static final String [] PACKAGES_TO_SCAN = {
                        MangoDomainModelMarker.class.getPackage().getName(),
                        CmbDomainModelMarker.class.getPackage().getName(),
                        CmbProductDomainModelMarker.class.getPackage().getName()
        };

        public static final String                                              PRODUCT_ENTITY_MANAGER = "productEntityManager";
        public static final String                                              PRODUCT_TX_MANAGER = "productTransactionManager";
        public static final String                                              SESSION_FACTORY_PRODUCT = "sessionFactoryProduct";
        // Default size of 10 is too small for CAB
        private static final int                                                MAX_POOL_SIZE = 250;
        private static final int                                                MAX_LIFETIME_SECONDS = 2 * 60; // XXX debug value
        //private static final int                                              MAX_LIFETIME_SECONDS = 60 * 60; // 1 hour
        private static final int                                                IDLE_TIMEOUT_SECONDS = 15 * 60;
        private static final int                                                LEAK_DETECTION_THRESHOLD_SECONDS = 30;
        private static final int                                                CONNECTION_TIMEOUT_SECONDS = 30;

        @Bean(name = SESSION_FACTORY_PRODUCT)
        @Primary
        public SessionFactory sessionFactoryProduct() {
                return sessionFactoryProductBean().getObject();
        }

        @Bean
        @Primary
    public LocalSessionFactoryBean sessionFactoryProductBean() {
        LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
        sessionFactory.setDataSource(productDataSource());
        sessionFactory.setPackagesToScan( PACKAGES_TO_SCAN );
        sessionFactory.setHibernateProperties(hibernateProperties());

        return sessionFactory;
    }

    @Bean
        @Primary
    public DataSource productDataSource() {
        /*
         * Get the PersistenceConfiguration from persistconfig.properties and
         * then build Creator to format the configuration data as needed.
         */
        PersistenceConfiguration cf = ProductConfig.getPersistenceConfiguration();
        if (cf == null || cf.getDataBaseName() == null)
                throw new ConfigurationException("Failed to load persistence configuration");
        HibernateP

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...