ANKIT KUMAR
ANKIT KUMAR

Reputation: 43

Concurrent Modification exception while evicting cache in Hibernate

My application maintains a cache of list of users and it is evicted when any new user is created. I am getting Concurrent Modification exception while evicting the cache.

Stack trace :

java.util.ConcurrentModificationException: null
    at java.util.HashMap.forEach(HashMap.java:1292)
    at org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl.releaseResources(ResourceRegistryStandardImpl.java:323)
    at org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor.afterTransaction(AbstractLogicalConnectionImplementor.java:60)
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.afterTransaction(LogicalConnectionManagedImpl.java:167)
    at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.afterCompletion(LogicalConnectionManagedImpl.java:291)
    at org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor.commit(AbstractLogicalConnectionImplementor.java:95)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:282)
    at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101)
    at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:627)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:633)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:386)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
    at com.mytestapp.service.impl.MyCacheManager$$EnhancerBySpringCGLIB$$6a5d7f13.resetTeamListCache(<generated>)
    at 
com.mytestapp.webapp.listener.StartupListener.refreshContext(StartupListener.java:199)
    at com.mytestapp..webapp.controller.admin.SystemUserManagementController.saveSystemUserData(SystemUserManagementController.java:529)

In my controller the code is as follows :

systemUserProfileManager.saveSystemUserProfile(userBasicInfo); // async call
StartupListener.refreshContext(request.getSession().getServletContext()); // cache evict

refreshContextMethod in StartupListener :

public static void refreshContext(ServletContext context) {
        ApplicationContext ctx = WebApplicationContextUtils
                .getRequiredWebApplicationContext(context);
        MyCacheManager myCacheManager = (MyCacheManager) ctx
                .getBean(myCacheManager);
        
        LOG.debug("Processing started for refreshing Servlet Context/ Cache!");
        // RESET All Cache
        myCacheManager.resetTeamListCache();
        LOG.debug("Processing completed for refreshing Servlet Context/ Cache!");
    }

resetTeamListCache method in MyCacheManager :

@CacheEvict(value = CacheConstants.SALES_TEAM_LIST, allEntries = true)
public void resetTeamListCache() {
    // Intentionally blank
}

Upvotes: 4

Views: 850

Answers (2)

I_AM__PAUL
I_AM__PAUL

Reputation: 138

This information is outdated; however, it took me several days to identify this issue.

This error can potentially block the Hikari connection and the connection stays as "active", leading to application failure at some point. The root cause lies in the cache.

In my particular scenario, I had configured the cache manager, but despite disabling 'spring.cache.type=none,' the cache continued to function inexplicably.

public CacheManager cacheManager() {
    ConcurrentMapCacheManager manager = new ConcurrentMapCacheManager();
    manager.setAllowNullValues(false);
    return manager;
}

My Service layer had also cache evict which caused then the exception and blocked connection to dabase.

 @Cacheable(value = "item", key = "#item.name", unless = "#result == null")

Upvotes: 0

Christian Beikov
Christian Beikov

Reputation: 16440

The error suggests that you are using the same Hibernate Session in multiple threads concurrently. The hash map, which according to the exception is iterated, does not change within the process of iteration, so the only way this can happen is if you have a concurrency problem in your application.

Upvotes: 2

Related Questions