I don't know if it is a normal behavior or not, but when I start the Spring server I check that the cache works correctly, at this point Ehcache informs me when it has correctly registered a model:
2020-11-06 21: 38: 23.253 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.model.bean.User
I'm noticing that Ehcache is registering the models for me twice:
2020-11-06 21: 38: 22.860 INFO 32643 --- [main] c.g.a.s.b.c.MultiCacheManagerConfig: Created cache memory with size (2)
2020-11-06 21: 38: 22.860 INFO 32643 --- [main] c.g.a.s.b.c.MultiCacheManagerConfig: Created cache disk with size (1)
2020-11-06 21: 38: 22.860 INFO 32643 --- [main] c.g.a.s.b.c.MultiCacheManagerConfig: Created cache with size (3)
2020-11-06 21: 38: 23.054 INFO 32643 --- [main] org.ehcache.core.EhcacheManager: Cache 'com.github.apolalca.spring.boot.model.bean.User' created in EhcacheManager.
2020-11-06 21: 38: 23.056 INFO 32643 --- [main] org.ehcache.core.EhcacheManager: Cache 'com.github.apolalca.spring.boot.repository.UserRepository.findByUsername' created in EhcacheManager.
2020-11-06 21: 38: 23.140 INFO 32643 --- [main] org.ehcache.core.EhcacheManager: Cache 'com.github.apolalca.spring.boot.model.bean.Blacklist' created in EhcacheManager.
2020-11-06 21: 38: 23.253 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.model.bean.User
2020-11-06 21: 38: 23.255 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.repository.UserRepository.findByUsername
2020-11-06 21: 38: 23.255 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.model.bean.Blacklist
2020-11-06 21: 38: 23.259 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.model.bean.User
2020-11-06 21: 38: 23.260 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.repository.UserRepository.findByUsername
2020-11-06 21: 38: 23.260 INFO 32643 --- [main] org.ehcache.jsr107.Eh107CacheManager: Registering Ehcache MBean javax.cache: type = CacheStatistics, CacheManager = urn.X-ehcache.jsr107-default-config , Cache = com.github.apolalca.spring.boot.model.bean.Blacklist
I have created a Listener that jumps when we create or modify the user (for example), this one does register a single creation, but from Eh107CacheManager I see that it is registered twice, why is this happening?
The cache has two configurations (it is my first configuration so it is very sure that if it is a failure it is my fault), I am creating two types of configurations, a memory configuration for user modles and another persistent type configuration (for save to disk) for blacklists (for example)
@Configuration
@EnableCaching
public class MultiCacheManagerConfig extends CachingConfigurerSupport {
private static final Logger LOG = LoggerFactory.getLogger(MultiCacheManagerConfig.class);
@Bean
public Blacklist blacklistBean() {
return new Blacklist();
}
@Bean
public org.springframework.cache.CacheManager cacheManager() {
//TODO: In case Multi Cluster -> Create createInMultiClusterCacheManager()
return new JCacheCacheManager(createInMemoryCacheManager());
}
private CacheManager getCacheManager(EhcacheCachingProvider provider, DefaultConfiguration configuration) {
return provider.getCacheManager(provider.getDefaultURI(), configuration);
}
private ClassLoader getClassLoader() {
return this.getClass().getClassLoader();
}
private CacheManager createInMemoryCacheManager() {
long cacheSize = 100;
long ttl = 200;
CacheEventListenerConfigurationBuilder cacheEventListenerConfiguration = CacheEventListenerConfigurationBuilder
.newEventListenerConfiguration(new CustomCacheEventLogger(), EventType.CREATED, EventType.UPDATED, EventType.EVICTED)
.unordered().asynchronous();
org.ehcache.config.CacheConfiguration<Object, Object> cacheConfiguration = CacheConfigurationBuilder
.newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder
.heap(cacheSize))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ttl)))
.withService(cacheEventListenerConfiguration)
.build();
org.ehcache.config.CacheConfiguration<Object, Object> cacheConfigurationDisk = CacheConfigurationBuilder
.newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder
.heap(cacheSize)
.disk(20, MemoryUnit.MB, true))
.withService(cacheEventListenerConfiguration)
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ttl)))
.build();
Map<String, CacheConfiguration<?, ?>> caches = createCacheConfigurations(cacheConfiguration);
LOG.info("Created cache memory with size({})", caches.size());
Map<String, CacheConfiguration<?, ?>> cachesDisk = createCacheConfigurationsDisk(cacheConfigurationDisk);
LOG.info("Created cache disk with size({})", cachesDisk.size());
caches.putAll(cachesDisk);
LOG.info("Created cache with size({})", caches.size());
EhcacheCachingProvider provider = getCachingProvider();
DefaultConfiguration configuration = new DefaultConfiguration(caches, getClassLoader(),
new DefaultPersistenceConfiguration(new File(
"{folder}/persistence",
"myUserData"
))
);
return getCacheManager(provider, configuration);
}
private Map<String, org.ehcache.config.CacheConfiguration<?, ?>> createCacheConfigurations(org.ehcache.config.CacheConfiguration<Object, Object> cacheConfiguration) {
Map<String, org.ehcache.config.CacheConfiguration<?, ?>> caches = new HashMap<>();
caches.put(com.github.apolalca.spring.boot.model.bean.User.class.getName(), cacheConfiguration);
caches.put(UserRepository.USERS_BY_LOGIN_CACHE, cacheConfiguration);
return caches;
}
private Map<String, org.ehcache.config.CacheConfiguration<?, ?>> createCacheConfigurationsDisk(org.ehcache.config.CacheConfiguration<Object, Object> cacheConfiguration) {
Map<String, org.ehcache.config.CacheConfiguration<?, ?>> caches = new HashMap<>();
caches.put(com.github.apolalca.spring.boot.model.bean.Blacklist.class.getName(), cacheConfiguration);
return caches;
}
private EhcacheCachingProvider getCachingProvider() {
return (EhcacheCachingProvider) Caching.getCachingProvider();
}
}
By the way, I am using ehcache 3 and spring 5 Greetings and thank you very much!
UPDATE: this problem should be resolved in the (upcoming) version 3.9.7, as per the issue mentioned below. Update your ehcache version or track the version releases in order to have the issue fixed.
After some investigation this seems to be because of the following bug: CacheConfiguration and CacheStatistics MBeans are registered again and again
What happens is that we want to use the combination of Spring and EhCache 3.
Unfortunately there is no org.springframework.cache.CacheManager
available for EhCache 3 in spring at this moment (only for v2, i.e. org.springframework.cache.ehcache.EhCacheCacheManager
), so we have to fall back to using the JCacheCacheManager
.
The JCacheCacheManager
has to initialize its caches (these are ~ proxying caches) internally as well, which is done through the afterPropertiesSet()
method inherited from InitializingBean
. Upon calling this afterPropertiesSet()
, after some indirection the loadCaches()
method is called, in which the following snippet of code occurs:
for (String cacheName : cacheManager.getCacheNames()) {
javax.cache.Cache<Object, Object> jcache = cacheManager.getCache(cacheName);
caches.add(new JCacheCache(jcache, isAllowNullValues()));
}
The relevant part is basically this: cacheManager.getCacheNames()
.
As can be read in the bug report, this method mistakenly disrespects the no-side-effects principle and re-initializes the caches.
TLDR: This is a known bug in EHCache v3, and an issue exists already. Maybe some day we will see a fix. I have also commented on the issue with a reference to this answer.