javahibernatewildflyinfinispansecond-level-cache

Wildfly failed to start when Hibernate query cache is active


We're migrating our application from JBoss EAP 6.4 to WildFly 14. We're experiencing problems with Hibernate and Infinispan.

The application is configured to use Hibernate's second-level cache (also called 2LC) with Wildfly-provided Infinispan.

With 2LC cache enabled but the query cache disabled, the application starts and seems to work correctly.

But when we try to enable again the query cache (property hibernate.cache.use_query_cache set to true), it crashes during its initialization, giving the joined stacktrace.

java.lang.ClassCastException: org.infinispan.hibernate.cache.v53.impl.DomainDataRegionImpl cannot be cast to org.hibernate.cache.spi.QueryResultsRegion

While debugging, I cannot understand why the internal code of Hibernate that manages the second-level cache tries to cast an entity cache region to a query result cache region.

I've tried to use separated caches for each, by defining the property hibernate.cache.infinispan.query.cfg with the cache local-query defined in the Infinispan configuration, but it seems Hibernate does not take it into account.

I'm pretty beginner in cache, I must confess that I don't fully understand what I'm doing, even after reading a lot of documentation.

Versions :

Here's the Maven dependencies declaration in our concerned pom.xml (the application is divided in several Maven project)

All these are in version 9.3.1.Final

This is the cache containers configuration stored in standalone.xml :

        <subsystem xmlns="urn:jboss:domain:infinispan:7.0">
            ...
            <cache-container name="hibernate" module="org.infinispan.hibernate-cache">
                <local-cache name="entity">
                    <transaction mode="NON_XA"/>
                    <object-memory size="10000"/>
                    <expiration max-idle="100000"/>
                </local-cache>
                <local-cache name="local-query">
                    <object-memory size="10000"/>
                    <expiration max-idle="100000"/>
                </local-cache>
                <local-cache name="timestamps"/>
            </cache-container>
        </subsystem>

The dependencies section of our jboss-deployment-structure.xml.

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
    <deployment>
        ...
        <dependencies>
            <!-- Infinispan -->
            <module name="org.infinispan" /> 
            <module name="org.infinispan.commons" />
            <module name="org.infinispan.hibernate-cache"/>

        </dependencies>
    </deployment>
</jboss-deployment-structure>

Our Infinispan configuration file :

<infinispan
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:infinispan:config:7.0 http://www.infinispan.org/schemas/infinispan-config-7.0.xsd"
    xmlns="urn:infinispan:config:7.0">
    <threads />

    <cache-container name="DefaultCacheManager"
        statistics="true">
        <transport />
        <jmx duplicate-domains="true" />

        <local-cache name="___defaultcache">
            <transaction mode="NONE" />
        </local-cache>

        <local-cache name="defaultCache">
            <transaction mode="NONE" />
            <expiration lifespan="1000" max-idle="1000" interval="500" />
            <memory>
                <object size="1000" />
            </memory>
            <persistence passivation="false">
                <file-store purge="false" read-only="false"
                    path="${jboss.server.temp.dir}/cacheservice" />
            </persistence>
        </local-cache>

        <local-cache name="local-query">
            <locking isolation="READ_COMMITTED" concurrency-level="1000"
                acquire-timeout="15000" striping="false" />
            <eviction max-entries="140000" strategy="LRU" />
            <expiration max-idle="1200000" />
            <transaction mode="NONE" auto-commit="false" />
        </local-cache>

        <!-- Other application caches ... -->

    </cache-container>
</infinispan>

Instead of starting correctly, the WildFly standalone instance crashes during its initialization, when attempting to mount the 2LC cache, and gives the following stack trace.

Setting hibernate.cache.use_query_cache to false fixes the problem, but we do need query cache.

Caused by: java.lang.ClassCastException: org.infinispan.hibernate.cache.v53.impl.DomainDataRegionImpl cannot be cast to org.hibernate.cache.spi.QueryResultsRegion
    at org.hibernate.cache.internal.EnabledCaching.makeQueryResultsRegionAccess(EnabledCaching.java:491)
    at org.hibernate.cache.internal.EnabledCaching.getQueryResultsCache(EnabledCaching.java:478)
    at org.hibernate.loader.Loader.listUsingQueryCache(Loader.java:2515)
    at org.hibernate.loader.Loader.list(Loader.java:2498)
    at org.hibernate.loader.criteria.CriteriaLoader.list(CriteriaLoader.java:109)
    at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1959)
    at org.hibernate.internal.CriteriaImpl.list(CriteriaImpl.java:370)
    at fr.bdf.interop.middle.dao.hibernate.HibernateGenericDao.find(HibernateGenericDao.java:110)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy204.find(Unknown Source)
    ...

Thanks,


Solution

  • Finally I found how to fix it. As Radim Vansa said, the problem came from the conflict between entities/collection cache region and query cache region.

    In Hibernate documentation relative to caching, I found that to define cache region name for a Query or a Criteria, setCacheRegion(String) method must be used.

    By searching all this methods calls in our code, I finally realized that in our abstract generic DAO class, we define query region name like this :

    public HibernateGenericDao(Class<E> type) {
        ...
        this.cacheRegion = type.getCanonicalName();
    }
    

    The cacheRegion field is then used here :

    criteria.setCacheRegion(getCacheRegion());
    

    Given that the entities/collections cache region name is also defined by entity class fully-qualified name (we don't know how), appending a suffix to the query cache region name resolve the conflict and make the application starting correctly.

    this.cacheRegion = type.getCanonicalName().concat(QUERY_CACHE_REGION_PREFIX);
    

    Thanks,