Simple Aged Cache的getExpired单元测试失败,求解决建议
Simple Aged Cache过期清理失效问题分析与解决
问题描述
实现了一个Simple Aged Cache,其余单元测试均通过,仅getExpired测试用例失败。测试场景为:向缓存存入两个条目,将时钟偏移3000毫秒后,预期缓存大小为1,但实际返回2。尝试在put方法前后调用cleanExpiredRecords方法,问题仍未解决。
缓存实现代码
package io.collective; import java.time.Clock; import java.util.HashMap; import java.util.Map; public class SimpleAgedCache { private final Clock clock; private final Map<Object, CacheEntry> cacheMap; public SimpleAgedCache(Clock clock) { this.clock = clock; this.cacheMap = new HashMap<>(); } public SimpleAgedCache() { this(Clock.system(Clock.systemDefaultZone().getZone())); } public void put(Object key, Object value, int retentionInMillis) { if (key != null && retentionInMillis > 0){ long expirationTime = clock.millis() + retentionInMillis; cacheMap.put(key, new CacheEntry(value, expirationTime)); } } public boolean isEmpty() { return cacheMap.isEmpty(); } public int size() { return cacheMap.size(); } public Object get(Object key) { cleanExpiredRecords(); CacheEntry entry = cacheMap.get(key); if (entry != null){ return entry.value; } return null; } private void cleanExpiredRecords(){ long currentTime = clock.millis(); cacheMap.entrySet().removeIf(entry -> entry.getValue().isExpired(currentTime)); } private static class CacheEntry{ Object value; long expirationTime; CacheEntry(Object value, long expirationTime){ this.value = value; this.expirationTime = expirationTime; } boolean isExpired(long currentTime){ return currentTime >= expirationTime; } boolean isNotExpired(long currentTime){ return !isExpired(currentTime); } } }
失败的测试用例
@Test public void getExpired() { TestClock clock = new TestClock(); SimpleAgedCache expired = new SimpleAgedCache(clock); expired.put("aKey", "aValue", 2000); expired.put("anotherKey", "anotherValue", 4000); clock.offset(Duration.ofMillis(3000)); assertEquals(1, expired.size()); assertEquals("anotherValue", expired.get("anotherKey")); }
问题原因
核心问题在于**size()方法未触发过期条目清理**:
- 当前只有
get()方法会调用cleanExpiredRecords()清理过期数据 - 测试用例中先调用
expired.size(),此时缓存中过期的aKey条目并未被清理,直接返回cacheMap的原始大小2,导致断言失败 - 后续调用
get()虽然会清理,但此时第一个断言已经失败
解决建议
- 修改
size()方法,添加过期清理逻辑:
public int size() { cleanExpiredRecords(); return cacheMap.size(); }
- 同步修改
isEmpty()方法,避免出现类似的状态不一致问题:
public boolean isEmpty() { cleanExpiredRecords(); return cacheMap.isEmpty(); }
- 可选优化:如果需要更高效的过期清理,可以考虑后台定时清理策略,但当前场景下,在查询缓存状态前主动清理已经能满足测试需求。
内容的提问来源于stack exchange,提问作者gdanylov
相关产品推荐
相关产品推荐

