我使用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”将方法的结果放入TreeCacheAspect @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
必须触摸每个父级的STRUCTURE
和DATA
条目。另外,也许更容易实现,TreeCache
可以确保在父项过期时将所有子项从缓存中删除。