task-152: 清理失败吞异常记指标(aiimage.temp-cleanup.failed 计数、日志+指标均不阻断主流程、按路径隔离、无重试循环)+ 8 条测试

This commit is contained in:
2026-09-02 06:29:59 +08:00
parent f0e12b9b1b
commit 3b34ec3bef
2 changed files with 144 additions and 1 deletions
@@ -2,8 +2,10 @@ package com.nanri.aiimage.modules.file.service;
import cn.hutool.core.io.FileUtil;
import com.nanri.aiimage.config.StorageProperties;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@@ -20,6 +22,7 @@ public class LocalTempCleanupService {
private static final Pattern ROOT_TEMP_FILE_PATTERN = Pattern.compile("^[a-fA-F0-9]{32}(\\.[^.]+)?$");
private static final String TRANSIENT_PAYLOAD_DIR_NAME = "transient-payload";
private static final String CLEANUP_FAILED_METRIC = "aiimage.temp-cleanup.failed";
private static final List<String> RESULT_DIR_NAMES = List.of(
"dedupe-result",
"convert-result",
@@ -32,6 +35,10 @@ public class LocalTempCleanupService {
private final StorageProperties storageProperties;
/** 指标注册表(可选注入:无注册表时仅记日志,不影响清理)。 */
@Autowired(required = false)
private MeterRegistry meterRegistry;
@Scheduled(cron = "${aiimage.storage.cleanup-cron:0 0 */6 * * *}")
public void cleanupLocalTempDir() {
if (!storageProperties.isCleanupEnabled()) {
@@ -78,7 +85,7 @@ public class LocalTempCleanupService {
deleteEmptyDirectories(child, tempDir);
}
} catch (Exception ex) {
log.warn("local temp cleanup failed: path={}", child.getAbsolutePath(), ex);
handleCleanupFailure(child, ex);
}
}
@@ -125,6 +132,23 @@ public class LocalTempCleanupService {
return ROOT_TEMP_FILE_PATTERN.matcher(file.getName()).matches();
}
/**
* 清理失败处理:记录日志与失败指标,不抛异常、不重试(下一轮定时清理自然重试)。
* 单个条目失败不影响其余条目的清理。
*/
void handleCleanupFailure(File child, Exception ex) {
log.warn("local temp cleanup failed: path={}", child == null ? null : child.getAbsolutePath(), ex);
try {
if (meterRegistry != null && child != null) {
meterRegistry.counter(CLEANUP_FAILED_METRIC, "path", child.getName()).increment();
}
} catch (Exception metricEx) {
// 指标记录失败也不阻断清理主流程
log.warn("local temp cleanup metric record failed path={}",
child == null ? null : child.getName(), metricEx);
}
}
private boolean isExpired(File file, Instant expireBefore) {
// 以 mtime 近似最后访问(TempFileMetadata 回退语义,与现状一致)
return TempFileMetadata.lastAccessTime(file).isBefore(expireBefore);