task-32: 将 task entity 本地缓存替换为有容量和过期回收的实现

This commit is contained in:
2026-08-29 20:11:16 +08:00
parent 5f7457a07b
commit 2cd5e07248
3 changed files with 221 additions and 1 deletions
@@ -7,6 +7,8 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "aiimage.task-pressure")
public class TaskPressureProperties {
private long localTaskEntityCacheMillis = 3000;
/** task entity 本地缓存容量上限,超限时按时间戳 LRU 淘汰最旧条目。 */
private int localTaskEntityCacheCapacity = 512;
// 本地文件缓存有效时长,超过该时长视为过期、强制回查 DB,避免陈旧 RUNNING 被复活
private long localTaskEntityFileCacheMillis = 60000;
private int dbSelectBatchSize = 200;
@@ -13,6 +13,7 @@ import org.springframework.stereotype.Service;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -145,7 +146,7 @@ public class ShopDataCrawlTaskCacheService {
return;
}
long now = System.currentTimeMillis();
taskEntityLocalCache.put(task.getId(), new LocalTaskEntityCacheEntry(
putLocalCache(task.getId(), new LocalTaskEntityCacheEntry(
now,
objectMapper.convertValue(task, FileTaskEntity.class)
));
@@ -159,6 +160,23 @@ public class ShopDataCrawlTaskCacheService {
}
}
/**
* 有界本地缓存写入:容量达到上限时按 cachedAtMillis LRU 淘汰最旧条目,
* 保证本地缓存内存有界。
*/
private void putLocalCache(Long taskId, LocalTaskEntityCacheEntry entry) {
taskEntityLocalCache.put(taskId, entry);
int capacity = Math.max(1, taskPressureProperties.getLocalTaskEntityCacheCapacity());
if (taskEntityLocalCache.size() > capacity) {
taskEntityLocalCache.entrySet().stream()
.sorted(Map.Entry.comparingByValue(
Comparator.comparingLong(LocalTaskEntityCacheEntry::cachedAtMillis)
.thenComparingLong(e -> e.task() == null ? 0L : e.task().getId() == null ? 0L : e.task().getId())))
.limit(taskEntityLocalCache.size() - capacity)
.forEach(entryToEvict -> taskEntityLocalCache.remove(entryToEvict.getKey()));
}
}
public Map<Long, FileTaskEntity> getTaskCacheBatch(java.util.List<Long> taskIds) {
Map<Long, FileTaskEntity> result = new LinkedHashMap<>();
if (taskIds == null || taskIds.isEmpty()) {
@@ -178,6 +196,10 @@ public class ShopDataCrawlTaskCacheService {
if (isLocalCacheFresh(cached, now)) {
result.put(taskId, objectMapper.convertValue(cached.task(), FileTaskEntity.class));
} else {
// 过期条目即时回收,避免本地缓存无限累积。
if (cached != null) {
taskEntityLocalCache.remove(taskId);
}
missingIds.add(taskId);
}
}
@@ -221,6 +243,11 @@ public class ShopDataCrawlTaskCacheService {
&& now - cached.cachedAtMillis() <= Math.max(0L, taskPressureProperties.getLocalTaskEntityCacheMillis());
}
/** 本地缓存当前条目数(测试与监控用)。 */
int localCacheSize() {
return taskEntityLocalCache.size();
}
private record LocalTaskEntityCacheEntry(long cachedAtMillis, FileTaskEntity task) {}
}