我使用infinispan 8.2.6.Final中的树缓存。我还有下一个服务:

public class TestService {
    public static final String FIRST_CACHE = "firstCache";

    private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

    @MCacheable(value = FIRST_CACHE, key = "/#{id}/first")
    public Object putInFirstCache(String id, String data) {
        log.info("putInFirstCache");
        return data;
    }

    @MCacheable(value = FIRST_CACHE, key = "/#{id}/second")
    public Object putInSecondCache(String id, String data) {
        log.info("putInSecondCache");
        return data;
    }

    @MCacheEvict(value = FIRST_CACHE, key = "/#{id}")
    public void evictFromCache(String id) {
        log.info("evictFromCache");
    }

}


Aspect @MCacheable通过treeCache.put(fqn, "data", result);通过fqn“ key”将方法的结果放入TreeCache

Aspect @MCacheEvict通过treeCache.removeNode(fqn);从缓存中删除值

方面的值是hashMap中TreeCache的关键。

所以,我为此写了一个简单的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CacheTestConfig.class, loader = AnnotationConfigContextLoader.class)
public class AspectTest {

    @Autowired
    private TestService testService;

    @Autowired
    private TreeCache treeCache;

    @Test
    public void testMCacheEvicts() throws InterruptedException {
        String id = UUID.randomUUID().toString();
        String data = System.currentTimeMillis() + "";

        testService.putInFirstCache(id, data);
        Thread.sleep(2000);
        testService.putInSecondCache(id, data);
        cacheDump(treeCache);

        Thread.sleep(3500);
        //miss here
        cacheDump(treeCache);
        assertEquals(null, treeCacheContainer.get(TestService.FIRST_CACHE).get(id + "/first", "data"));
        testService.evictFromCache(id);
        cacheDump(treeCache);
        assertEquals(null, treeCacheContainer.get(TestService.FIRST_CACHE).get(id + "/second", "data"));

    }

    private void cacheDump(TreeCache<String, Object> treeCache) {
        StringBuilder sb = new StringBuilder("{\r\n");
        for (Map.Entry<?, ?> entry : treeCache.getCache().entrySet()) {
            sb.append("[ ").append(entry.getKey()).append(",").append(entry.getValue()).append(" ]\r\n");
        }
        sb.append("}\r\n");

        System.out.println("Cache dump: " + sb.toString());
    }
}


缓存的配置为:

@Bean
public DefaultCacheManager cacheManager() {
    org.infinispan.configuration.cache.Configuration cfg = new ConfigurationBuilder()
            .jmxStatistics().enable()
            .eviction().strategy(EvictionStrategy.LRU).type(EvictionType.COUNT).size(5000)
            .expiration().wakeUpInterval(2, TimeUnit.SECONDS).lifespan(5, TimeUnit.SECONDS).maxIdle(100, TimeUnit.SECONDS)
            .invocationBatching().enable()
            .build();
    return new DefaultCacheManager(cfg, true);
}


这个测试的结果很奇怪:

Cache dump: {
[ NodeKey{DATA, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5/second},AtomicHashMap{size=1} ]
[ NodeKey{STRUCTURE, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5/second},AtomicHashMap{size=0} ]
[ NodeKey{DATA, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5},AtomicHashMap{size=0} ]
[ NodeKey{STRUCTURE, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5},AtomicHashMap{size=2} ]
[ NodeKey{DATA, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5/first},AtomicHashMap{size=1} ]
[ NodeKey{STRUCTURE, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5/first},AtomicHashMap{size=0} ]
[ NodeKey{STRUCTURE, fqn=/},AtomicHashMap{size=1} ]
[ NodeKey{DATA, fqn=/},AtomicHashMap{size=0} ]
}

Cache dump: {
[ NodeKey{DATA, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5/second},AtomicHashMap{size=1} ]
[ NodeKey{STRUCTURE, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5/second},AtomicHashMap{size=0} ]
[ NodeKey{STRUCTURE, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5},AtomicHashMap{size=2} ]
}

removed: false, exists: false
Cache dump: {
[ NodeKey{DATA, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5/second},AtomicHashMap{size=1} ]
[ NodeKey{STRUCTURE, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5/second},AtomicHashMap{size=0} ]
[ NodeKey{STRUCTURE, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5},AtomicHashMap{size=2} ]
[ NodeKey{DATA, fqn=/e4c3b326-3205-4472-bbc7-3ae50e72ebb5/first},AtomicHashMap{size=0} ]
}

com.test.AspectTest > testMCacheEvicts FAILED
    java.lang.AssertionError at AspectTest.java:55

expected:<null> but was:<1489165005306>
Expected :null
Actual   :1489165005306
 <Click to see difference>


谁知道infinispan为什么在存在子级的情况下使缓存的根节点过期?

最佳答案

简单的答案是,因为TreeCache实现中没有任何内容可以处理过期问题。

基础缓存会分别使每个条目到期,并且在修改子项以保持它们存活时,TreeCache必须触摸每个父级的STRUCTUREDATA条目。另外,也许更容易实现,TreeCache可以确保在父项过期时将所有子项从缓存中删除。

09-05 03:34