如何以编程方式配置L2 Hibernate缓存与ECache?

huangapple go评论49阅读模式
英文:

How to programmatically configure L2 Hibernate caches with ECache?

问题

I can provide a summary of the issue and some possible solutions:

You're facing problems while trying to configure L2 caching with custom settings in Hibernate 6.1.7 within your Spring 6 application using Ehcache 3.10.x. It seems that when you change the CacheConcurrencyStrategy, you encounter exceptions, and the cache behavior becomes unreliable.

Possible solutions or directions to explore:

  1. Review Cache Configuration: Double-check your cache configuration for consistency, including entity annotations, cache names, and concurrency strategies. Ensure that your entity classes and cache settings are aligned.

  2. Concurrency Strategy: It appears that changing the CacheConcurrencyStrategy leads to issues. Review the documentation for Hibernate and Ehcache to ensure that you're using the correct strategy for your use case. It might be necessary to choose a strategy that matches your entity and usage patterns.

  3. Debugging: Debug the code to understand why the exceptions occur when changing the concurrency strategy. Investigate which part of the code is causing the class cast exceptions and consider modifying the code accordingly.

  4. Consult the Community: Seek help and advice from the Hibernate and Ehcache communities or forums. Others might have encountered similar issues or have insights into the best practices for configuring L2 caching with custom settings.

  5. Upgrade: Check if there are newer versions of Hibernate and Ehcache available that might address these issues or provide better documentation and support for custom L2 caching configurations.

Remember to keep backups of your code and configurations before making significant changes, and test any modifications in a controlled environment to avoid potential issues in production.

英文:

I'm trying to configure L2+QueryCache for Hibernate 6.1.7 in my Spring 6 app running Ehcache 3.10.x

In my app, Spring-level caches with method-level @Cacheable, @CachePut, @CacheEvict, etc. work fine. I create the caches and they work as expected.

L2 cache also works correctly, but only if I don't create any L2 caches and let Spring create the default ones (missing_cache_strategy: create). This is presentig heap-related problems in production because the default cache settings in Ehcache are allowing infinite heap usage.

How do I create L2 caches with my own configs, from code? I want to avoid the xml Ehcache config at this time, and Ehcache seems to be moving away from it anyway.

This is how I configure Spring-level caches, and it works:

    @Configuration
    @EnableCaching
    public class MyCacheConfig {
      @Bean
      public CacheManager ehCacheManager() {
          CacheManager cacheManager = Caching.getCachingProvider().getCacheManager();

         //Create a Spring-level cache
         var config = CacheConfigurationBuilder
                .newCacheConfigurationBuilder(SomeMethodArg.class, SomeMethodReturnType.class)
                .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMinutes(30))
                .build();
          cacheManager.createCache("my_spr_cache", Eh107Configuration.fromEhcacheCacheConfiguration(config));

          return cacheManager;
      }
    }

Following the Ehcache docs and other sources, I was able to create L2 caches in a similar manner by adding another cache to the bean above:

//Create a L2 cache
var config = CacheConfigurationBuilder
                .newCacheConfigurationBuilder(CacheKeyImplementation.class, AbstractReadWriteAccess.Item.class)
                .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMinutes(30))
                .build();
        
//L2 cache name should be the fully qualified name of the Entity
cacheManager.createCache("com.mydomain.model.MyClass", Eh107Configuration.fromEhcacheCacheConfiguration(config));

And of course, the MyClass Entity is annotated with:

@jakarta.persistence.Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) //Note that some other classes instead use NONSTRICT_READ_WRITE 

Now the cache I made is correctly created at runtime and Ehcache doesn't build a default one. I can confirm this from the startup logs - manually created caches are logged significantly earlier on.

Now the issue: this method is unreliable. Firstly, it seems that changing the ConcurrencyStrategy breaks the cache creation code, for example, switching to NONSTRICT_READ_WRITE on the entity will cause Exceptions:

Exception in thread "(3/16)" java.lang.ClassCastException: Invalid value type, 
expected:  org.hibernate.cache.spi.support.AbstractReadWriteAccess$Item 
but was :  org.hibernate.cache.spi.entry.StandardCacheEntryImpl

The first attempt I made was obviously following the exception literally and changing the value class to StandardCacheEntryImpl. This just causes even more erratic behavior, with the cache behaving fine at low usage but failing when consistent load is applied:

Caused by: java.lang.ClassCastException: Invalid value type, 
expected : org.hibernate.cache.spi.support.AbstractReadWriteAccess$Item 
but was : org.hibernate.cache.spi.support.AbstractReadWriteAccess$SoftLockImpl

If I switch those two classes back and forth, I get the same exception but reversed.

This leads me to believe that I'm using the wrong approach entirely. I can't find any modern and updated question on this, could someone kindly point me in the right direction?


If it can be useful, this is the relevant config in my application.yml:

# [More settings here...]
  jpa:
    # [More settings here...]
    properties:
      jakarta:
        persistence:
          sharedCache:
            #required - enable selective caching mode - only entities with @Cacheable annotation will use L2 cache.
            mode: ENABLE_SELECTIVE
      hibernate:
        cache:
          use_second_level_cache: true
          use_query_cache: true
          region:
            factory_class: org.hibernate.cache.jcache.JCacheRegionFactory
        jakarta:
          cache:
            missing_cache_strategy: create

答案1

得分: 0

For cache keys, Hibernate 可能会使用三种不同的实现(请检查 org.hibernate.cache.spi.CacheKeysFactory 接口以及其 createCollectionKeycreateEntityKeycreateNaturalIdKey 方法),此外,Hibernate 允许通过 hibernate.cache.keys_factory 设置来覆盖默认的 CacheKeysFactory,所以从技术上讲,您无法猜测特定缓存区域将使用哪种缓存键实现,而且缓存区域可能包含不同的数据,因此 Object.class 也是缓存键实现的有效定义。

对于缓存值的实现也可能不同:

  • 对于自然ID缓存,Hibernate 在那里存储实体标识符
  • 对于其他缓存,可以是 MapStandardCacheEntryImplorg.hibernate.cache.spi.support.AbstractReadWriteAccess.Lockable 的实现

所以,对于缓存值,Object.class 也是唯一有效的定义。

英文:

For cache keys Hibernate may potentially use three different implementations (please check org.hibernate.cache.spi.CacheKeysFactory interface and it's createCollectionKey, createEntityKey and createNaturalIdKey methods), moreover, Hibernate, allows to override default CacheKeysFactory via hibernate.cache.keys_factory setting, so, technically you have no chance to guess what cache key implementation will be used for particular cache region, moreover cache regions may contain different data, so, Object.class is a valid definition of cache key implementation.

In case of cache values implementations may also differ:

  • for natural id cache Hibernate stores entity identifiers there
  • for other caches that can be either Map or StandardCacheEntryImpl or implementation of org.hibernate.cache.spi.support.AbstractReadWriteAccess.Lockable

so, for cache values Object.class is the only valid definition as well.

huangapple
  • 本文由 发表于 2023年5月7日 04:21:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76190958.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定