feat(cache): capacity LRU for image cache (CACHE-2)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-29 17:16:50 +08:00
1 parent 85b352b8eb
commit d13c18b4b1
3 files changed
+186 -22

No files matched your search

+23 -2
View File
@@ -12,7 +12,7 @@ import type { z } from "zod";
import { dataPaths } from "../../config.ts";
import { requireScope } from "../../http/auth.ts";
import { errorBody } from "../../http/errors.ts";
import { CACHE_REGIONS, sharedCache, tmdbCachePolicy } from "../../shared-cache.ts";
import { CACHE_REGIONS, sharedCache, storageSettings, tmdbCachePolicy } from "../../shared-cache.ts";
import { registerScheduledJob } from "../schedules/registry.ts";
import type { ModuleApp, ServerContext, ServerModule } from "../types.ts";
import { ImageCache, ImageProxyError } from "./images.ts";
@@ -22,7 +22,8 @@ import { ImageCache, ImageProxyError } from "./images.ts";
* - PG 缓存 kv_cache(UNLOGGED,移植 MP TTLCache)的过期行物理清理任务 cache.sweep(每 10 分钟);
* - TMDB 缓存管理(对照 MP tmdb_cache_items / tmdb_cache_clear):GET /cache/tmdb、POST /cache/tmdb/clear;
* - 图片代理 GET /img/tmdb/{size}/{file}(会话或 API Key read,移植 MP ImageHelper / fetch_image)+ DATA_DIR/cache/images 落盘,
* 用量 / 清空 GET /cache/images、POST /cache/images/clear,按保留天数清理的任务 cache.images_clean(每天)。
* 用量 / 清空 GET /cache/images、POST /cache/images/clear,按保留天数清理的任务 cache.images_clean(每天);
* - CACHE-2:图片缓存容量 LRU 任务 cache.images_lru(30 分钟,预算 storage.budgets.imagesGb,超出删最久未访问)。
*/
export interface CacheModuleOptions {
/** 图片上游 fetch(测试注入) */
@@ -203,6 +204,26 @@ export function createCacheModule(opts: CacheModuleOptions = {}): ServerModule &
return { summary: `删除 ${n} 张过期图片` };
},
});
registerScheduledJob(ctx, {
key: "cache.images_lru",
defaults: {
name: "图片缓存容量控制",
description: "图片代理缓存超过容量预算(存储设置,缺省 20 GB)时,按最久未访问删除到预算的 90%",
group: "maintain",
schedule: { type: "interval", everyMs: 30 * 60_000 },
firstRun: "after_interval",
},
run: async () => {
const { imagesGb } = (await storageSettings(ctx)).budgets;
const r = await images(ctx).enforceBudget(Math.round(imagesGb * 1024 ** 3));
const gb = (b: number) => `${Math.round((b / 1024 ** 3) * 100) / 100} GB`;
return {
summary: r.deleted
? `删除 ${r.deleted} 张最久未访问的图片,释放 ${gb(r.freedBytes)},现用 ${gb(r.usedBytes)}`
: `现用 ${gb(r.usedBytes)},未超过预算 ${imagesGb} GB`,
};
},
});
},
};
}