0

There is an application with MySQL, Hibernate and HicariCP combo for interaction with database using JTA session management paradigm. Application running using WildFly application server. Configuration looks like following

@Configuration
@EnableTransactionManagement
@ComponentScan("XXX")
public class DatabaseConfig {

  @Bean
  public LocalSessionFactoryBean sessionFactory(CustomDatasource datasource) {
    LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
    sessionFactory.setDataSource(datasource);
    sessionFactory.setPackagesToScan("XXX");
    sessionFactory.setHibernateProperties(hibernateProperties());
    return sessionFactory;
  }

  @Bean
  public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
    HibernateTransactionManager transactionManager = new HibernateTransactionManager();
    transactionManager.setSessionFactory(sessionFactory);
    return transactionManager;
  }

  @Bean
  public DefaultMergeEventListener mergeEventListener() {
    return new DefaultMergeEventListener();
  }

  private Properties hibernateProperties() {
    Properties properties = new Properties();
    properties.put("hibernate.current_session_context_class", "jta");
    properties.put("hibernate.transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory");
    properties.put("hibernate.transaction.manager_lookup_class", "org.hibernate.transaction.JBossTransactionManagerLookup");
    properties.put("hibernate.transaction.jta.platform", "JBossAS");
    
    return properties;
  }

} 

After upgrading Hibernate from 3.6.10.Final to 5.1.17.Final we noticed that number of connections retrieved from connection pool considerably increased. For verification purposes we've added a counter on how many times javax.sql.Datasource.getConnection() is executed.

We executed certain business flow which involves multiple APIs and compared results before and after upgrade. And it's interesting that number of connections acquired per API is exactly doubled.

In terms of what have changed - just dependency version upgrade, updated several hibernate classes packages and added @Transactional annotation to Dao/Repository level. Nothing else.

Wondering what have changed internally within Hibernate which caused this difference? Appreciate any help!

alex_bondar
  • 422
  • 2
  • 5
  • 18

2 Answers2

1

As far as I know, there was no change. If you say the amount doubled, I would check if the transaction integration is somehow "broken". Maybe you are creating 2 transactions whereas before you created just a single one. When querying with a Hibernate Session/EntityManager then, you might see this because previously you had a single connection for a longer time.

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58
  • Thank you for your answer. I've added more precise metrics collector and seeing 25% connection retrieval increase. And here are 2 new traces that seems to contribute into 25% increase: `SessionImpl@11.initializeCollection#2004 – alex_bondar Mar 19 '21 at 04:54
  • Also worth to mention that we are using HibernateTemplate as opposed to classic `sessionFactory.getCurrentSession()`. I'll check if it makes any difference here – alex_bondar Mar 19 '21 at 04:58
  • It's really hard to say without seeing the code or being able to debug it. All I can tell you, is that this is most probably cause by some integration code as it seems to be a systematic change. – Christian Beikov Mar 22 '21 at 14:51
0

From what I can tell so far, gist of change lies within connection allocation mechanism. To better illustrate idea here is a code snippet:

  class UserService {
    public void createUser() {
      
      userRepository.saveUser();
      
      avatarService.createUserAvatar();
      invalidateAvatarCache();
      
      addressService.createUserAddress();
      invalidateAddressCache();
      
      paymentService.saveUserPaymentInfo();
      thirdpartyService.sendNotification();
    }
  }

This is not very good practice, but current user creation process is phased. Meaning, entities are persisted to DB one by one instead of commit/rollback everything at once. There are multiple downsides of current approach, but that's where we are. Before (with Hibernate 3.6.x) we were not using @Transactional at all. But with upgrade we need to annotate at least write operations with @Transactional. So, every underlying method in Repository/Dao that flushes data (userRepository.saveUser, avatarService.createUserAvatar, addressService.createUserAddress and paymentService.saveUserPaymentInfo) is marked with @Transactional.

With that being said, connection is being acquired every time we enter transactional method which results in overall higher connection usage. Just for sake of experiment I've added @Transactional to whole createUser method and with that number of connections acquired/released seems to be on par with what it was before for a given API. But obviously we can't proceed with this option, because as a part of code flow we invalidate various caches, notify external services. Which means for case of a failure, notifications and data in cache will be inconsistent with what is stored in DB.

So, now I'm looking for an option to re-use already acquired connection. Any ideas?

P.S. slighly related to the topic - When are connections returned to the connection pool with Spring JPA (Hibernate) Entity Manager?

alex_bondar
  • 422
  • 2
  • 5
  • 18