0

I managed to join EhCache with Terracota for distributed clustering (item at https://github.com/ehcache/ehcache3/releases/download/v3.4.0/ehcache-clustered-3.4.0-kit.zip) and make an Spring Configuration Integration like:

import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.cache.CacheManager;
import javax.cache.Caching;
import org.ehcache.clustered.client.config.ClusteredStoreConfiguration;
import org.ehcache.clustered.client.config.builders.ClusteredResourcePoolBuilder;
import org.ehcache.clustered.client.config.builders.ClusteringServiceConfigurationBuilder;
import org.ehcache.clustered.client.config.builders.ServerSideConfigurationBuilder;
import org.ehcache.clustered.common.Consistency;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.MemoryUnit;
import org.ehcache.core.config.DefaultConfiguration;
import org.ehcache.expiry.Expirations;
import org.ehcache.jsr107.EhcacheCachingProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import net.crezco.util.CustomProperties;

@Configuration
@EnableCaching
@Component
public class CacheConfig extends CachingConfigurerSupport {

    private CustomProperties props;

    private CacheManager cacheManager;

    @Autowired
    public CacheConfig(CustomProperties props) {
        this.props = props;
    }

    @Bean
    @Override
    public org.springframework.cache.CacheManager cacheManager() {
        cacheManager = createClusteredCacheManager();
        return new JCacheCacheManager(cacheManager);
    }

    private CacheManager createClusteredCacheManager() {
        URI clusterUri = URI.create(props.readCacheLocation());
        Consistency consistency = Consistency.STRONG;
        long sizeInMb = 10;
        int ttl = 90; // 90s
        long size = 100;

        ClusteringServiceConfigurationBuilder clusteringServiceConfigurationBuilder = ClusteringServiceConfigurationBuilder.cluster(clusterUri);
        ServerSideConfigurationBuilder serverSideConfigurationBuilder = (clusteringServiceConfigurationBuilder.autoCreate()).defaultServerResource("main");

        ResourcePoolsBuilder.newResourcePoolsBuilder();
        org.ehcache.config.CacheConfiguration<Object, Object> cacheConfiguration = CacheConfigurationBuilder
                .newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(size).with(ClusteredResourcePoolBuilder.clusteredDedicated(sizeInMb, MemoryUnit.MB)))
                .withExpiry(Expirations.timeToLiveExpiration(new org.ehcache.expiry.Duration(ttl, TimeUnit.SECONDS))).add(new ClusteredStoreConfiguration(consistency)).build();

        Map<String, org.ehcache.config.CacheConfiguration<?, ?>> caches = createCacheConfigurations(cacheConfiguration);

        EhcacheCachingProvider provider = getCachingProvider();
        DefaultConfiguration configuration = new DefaultConfiguration(caches, provider.getDefaultClassLoader(), serverSideConfigurationBuilder.build());
        return provider.getCacheManager(provider.getDefaultURI(), 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("iterableCountryCache", cacheConfiguration);
        caches.put("elementCountryCache", cacheConfiguration);
        return caches;
    }

    private EhcacheCachingProvider getCachingProvider() {
        return (EhcacheCachingProvider) Caching.getCachingProvider();
    }
}

Then, on the Manager:

@Cacheable(cacheNames = "elementCountryCache", key = "#id + #langCode")
public CountryRes getById(Long id, User user, String langCode) {
    return getByIdTemplate(id, user, langCode);
}

@Cacheable(cacheNames = "iterableCountryCache", key = "#query.toCacheKeyString()")
public Iterable<CountryRes> findAll(SearchQuery<CountryRes> query, User user, String langCode) {
    return super.findAll(query, user, langCode);
}

@CacheEvict(value = "iterableCountryCache", allEntries = true)
public CountryRes save(CountryRes res, User user, String langCode) {
    return saveTemplate(res, user, langCode);
}

@Caching(evict = { @CacheEvict(value = "iterableCountryCache", allEntries = true), @CacheEvict(value = "elementCountryCache", key = "#res.getNativeId() + #langCode") })
public CountryRes update(CountryRes res, User user, String langCode) {
    return updateTemplate(res, user, langCode);
}

@Caching(evict = { @CacheEvict(value = "iterableCountryCache", allEntries = true), @CacheEvict(value = "elementCountryCache", allEntries = true) })
public void deleteById(Long id, User user) {
    deleteTemplate(id, user);
}

And Caching seems to work properly. When testing (Junit or manually starting tomcat) findAll & findById functions speed up a lot at after first attempt.

But when trying to save, an Exception occurs:

java.lang.UnsupportedOperationException: Implement me
    at org.ehcache.clustered.client.internal.store.ClusteredStore.iterator(ClusteredStore.java:463)
    at org.ehcache.impl.internal.store.tiering.TieredStore.iterator(TieredStore.java:243)
    at org.ehcache.core.Ehcache$Jsr107CacheImpl.removeAll(Ehcache.java:832)
    at org.ehcache.jsr107.Eh107Cache.removeAll(Eh107Cache.java:311)
    at org.springframework.cache.jcache.JCacheCache.clear(JCacheCache.java:108)
    at org.springframework.cache.interceptor.AbstractCacheInvoker.doClear(AbstractCacheInvoker.java:113)
    at org.springframework.cache.interceptor.CacheAspectSupport.performCacheEvict(CacheAspectSupport.java:457)
    at org.springframework.cache.interceptor.CacheAspectSupport.processCacheEvicts(CacheAspectSupport.java:445)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:402)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:314)
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
    at net.crezco.service.core.CountryManager$$EnhancerBySpringCGLIB$$d444b04b.save(<generated>)
    at net.crezco.service.core.CountryManagerTest.createCountryCached(CountryManagerTest.java:217)
    at net.crezco.service.core.CountryManagerTest.testBadUpdate(CountryManagerTest.java:123)
    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.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Any idea here? Thank you very much in advance.

Fibman
  • 126
  • 9
  • 1
    https://github.com/ehcache/ehcache3/issues/2364 Was an ehcache bug. – Fibman Apr 26 '18 at 07:12
  • Still is as per this writing. And it renders useing a terracotta cluster with Spring basically unusable, since with the new versions of EhCache you need to use the JSR107 interface and evictAll is quite an important feature that you often need. :(( – marc82ch Oct 17 '18 at 07:55

1 Answers1

0

Seems that removing .with(ClusteredResourcePoolBuilder.clusteredDedicated(sizeInMb, MemoryUnit.MB)) makes the iterator be fully accessed. But I don't know why nor the reason because it's not working properly with it.

Fibman
  • 126
  • 9