docs: 统一元数据缓存运行手册与 MP 差异
runbook/cache.md 重写为统一缓存说明(两级、策略表、过期先用旧值、同请求合并、pinned 与预热、跨进程失效、命中统计、清空); storage.md 补新 TTL 字段;moviepilot-tmdb-cache.md / moviepilot-discover.md 记与 MP 的差异。 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
81dca5d664
commit
850034b87a
4 files changed
+142
-25
No files matched your search
@@ -46,7 +46,7 @@
|
||||
| 上游 | 我们 |
|
||||
| --- | --- |
|
||||
| 每个请求 `@cached(maxsize=CONF.douban, ttl=CONF.meta, skip_none=True)`,键 = 函数 + 参数 | 列表 / 搜索请求经 `MetaRequestCache` 端口 → 服务端 `cachedCall`(region `douban.request` / `bangumi.request`,上限 5000 条);键 = `方法 + 参数排序后的地址 + 请求体`(`metaRequestKey`) |
|
||||
| TTL 只有一个 `CONF.meta` | 按类别取 `storage.cacheTtl`:搜索 `metaSearchDays`(缺省 1 天)、榜单 / 热门 / 每日放送 / 趋势页 `metaListHours`(缺省 6 小时)、空结果 `metaEmptyHours`(缺省 1 小时,不超过正常 TTL) |
|
||||
| TTL 只有一个 `CONF.meta` | 按类别取 `storage.cacheTtl`(统一元数据缓存策略表,见 runbook/cache.md):搜索 `metaSearchDays`(缺省 7 天,空结果 `metaMissDays` 1 天)、榜单 / 热门 / 每日放送 / 趋势页 `metaListHours`(缺省 6 小时,空结果 `metaEmptyHours` 1 小时),空结果 TTL 不超过正常 TTL;过期先用旧值后台刷新 |
|
||||
| `skip_none`:请求失败返回 None 不缓存 | 网络失败、非 2xx(含 403)、返回的不是 JSON(风控 / 验证页)、趋势页不是浏览页,适配器都抛错,`cachedCall` 不写 |
|
||||
| 条目详情同样走请求缓存 | 条目详情**不走**请求缓存:已有条目级缓存 `douban.subject` / `bangumi.subject`(30 天,存归一化后的标题 / 别名 / 年份,还收站点详情页与网页搜索得到的条目),再缓存一遍原始响应就是同一份数据存两遍;`meta.subject_miss`(6 小时)保留,是识别升级时防止反复打外站的负缓存,不属于请求缓存 |
|
||||
| 限流:无(靠缓存) | 每源进程内限流(豆瓣 2 并发、起始间隔 300 ms;bgm 3 并发、100 ms),只包真正发出的网络请求,缓存命中不占额度 |
|
||||
|
||||
@@ -72,3 +72,21 @@ PG 读写失败时 TMDB 路径按未命中处理(内存缓存不会失败)。
|
||||
偏离:MP 失败时返回空响应,我们回 502(前端回退占位);CACHE-2 加了容量 LRU(`cache.images_lru`,按 atime,预算 `storage.budgets.imagesGb`),
|
||||
按天清理保留作兜底;MP 按配置走代理服务器,我们直连(TMDB API 也未走代理);
|
||||
MP 只在启动时清理,我们改为每天一次的定时任务。
|
||||
|
||||
## 统一元数据缓存(2026-10-06)与 MP 的差异
|
||||
|
||||
用户定:TMDB / 豆瓣 / Bangumi 共用一套缓存策略,更积极地缓存,不上 Redis(PG `kv_cache` 二级 + 进程内存一级)。
|
||||
键、TTL 语义(正常 / 空结果短 TTL)、失败不缓存与上面各表一致;以下是 MP 没有、我们加的部分(详见 [runbook/cache.md](../runbook/cache.md)):
|
||||
|
||||
| 项 | MP | 我们 | 理由 |
|
||||
|---|---|---|---|
|
||||
| 存储层级 | 单进程内存 `TLRUCache`(可选 Redis) | L1 进程内存(每 region 1000 条、5 分钟)+ L2 PG `kv_cache`;读 L1 → L2 → 源,写两级 | 三个进程共享 L2,热点走 L1 少一次 PG 往返 |
|
||||
| TTL | `META_CACHE_EXPIRE` 一个值 + `EMPTY_RESULT_CACHE_TTL` | 「来源 × 资源类型」策略表:搜索 7 天 / 详情 30 天 / 列表 6 小时 / 人物与图片 90 天,空结果 1 天(列表 1 小时),取 `storage.cacheTtl` | 用户要求更积极地缓存,不同资源变化频率差很多 |
|
||||
| 过期 | 过期即未命中 | 宽限期(TTL 的 50%,最多 7 天)内先返回旧值、后台刷新;刷新失败继续用旧值并记警告 | 元数据过期后通常仍可用,不让请求等外站;外站故障时不降级 |
|
||||
| 并发 | `@cached` 无合并(TMDB 客户端内存 LRU 有) | 同进程同 key 只回源一次(含后台刷新) | 列表页 / 批量识别并发多 |
|
||||
| 淘汰 | TLRU | TLRU,但关注中作品的 TMDB 条目 `pinned` 不参与 LRU,命中续期 | 关注作品的详情 / 季 / 剧集组要一直在 |
|
||||
| 预热 | 无 | 作品进入关注时排 `prewarmMetadata`:详情、各季、剧集组、海报 / 背景 / 标志 | 订阅 / 下载后马上要刷新与刮削 |
|
||||
| 失效 | 进程内,`clear_cache()` | `PgCache.delete / clear` 发 `pg_notify('umt_cache_invalidate')`,各进程丢 L1;监听断开时 L1 停用,重连后整体清空 | 多进程下 L1 不能拿旧值 |
|
||||
| 统计 | 无 | 按 region 计 L1 / L2 / 旧值 / 未命中 / 回源失败 / 后台刷新,每分钟写 `cache_stat`,设置页显示近 7 天命中率 | 看缓存是否起作用 |
|
||||
| 豆瓣 / bgm 条目查询失败 | `DoubanApi` / `BangumiApi` 失败不缓存 | 同;只有「条目不存在」写 `meta.subject_miss`(以前网络失败也记 6 小时负缓存),失败后本进程同一条目冷却 30 分钟 | 策略表「失败不缓存」 |
|
||||
| TMDB 详情请求 | `append_to_response` 不含 images | 富详情加 `images`(`include_image_language=zh,en,null`),原语言不是中英文时另取 `/images`,选标志写 `clearlogo.png` | 用户定:不接 fanart.tv,用 TMDB logos;请求键变化后旧的富详情缓存会各自 miss 一次 |
|
||||
+108
-19
@@ -1,6 +1,6 @@
|
||||
# 缓存与限流运行手册(C2-A)
|
||||
# 缓存与限流运行手册
|
||||
|
||||
不引入 Redis / Deno KV。跨进程共享的状态都放 PG:站点限流令牌桶、TMDB 缓存。缓存语义照搬 MoviePilot,
|
||||
不引入 Redis / Deno KV。跨进程共享的状态都放 PG:站点限流令牌桶、缓存。缓存语义照搬 MoviePilot,
|
||||
对照与偏离见 [docs/porting/moviepilot-tmdb-cache.md](../porting/moviepilot-tmdb-cache.md)。
|
||||
|
||||
## 跨进程站点限流
|
||||
@@ -14,27 +14,113 @@
|
||||
|
||||
排查:`select * from rate_limit_bucket where key = 'site:<id>'`;`blocked_until` 不为空说明站点要求暂停。
|
||||
|
||||
## PG 缓存(kv_cache)
|
||||
## 统一元数据缓存
|
||||
|
||||
- 表 `kv_cache`(迁移 `0042_cache`,`UNLOGGED`:不写 WAL,崩溃后清空):(region, key) 主键,value jsonb,
|
||||
expires_at 每条独立,accessed_at 为最近读写时间。
|
||||
- `@umt/db` 的 `PgCache`(对照 MP `TTLCache`):set 带 ttl(秒,≤ 0 删除)与 maxsize(按 region 计条数,超出先删过期再删最久未用);
|
||||
get 命中刷新最近使用;`cachedCall` 对照 MP `@cached`(skip_none / skip_if / empty_ttl / fresh)。
|
||||
- 定时任务 `cache.sweep`(维护组,每 10 分钟)物理删除过期行。
|
||||
TMDB / 豆瓣 / Bangumi 三个来源共用一套缓存层(`@umt/db` 的 `MetadataCache`,服务端入口 `apps/server/src/shared-cache.ts`)。
|
||||
用户 2026-10-06 定:更积极地缓存,不上 Redis;PG `kv_cache`(UNLOGGED)作二级,进程内存作一级。
|
||||
|
||||
### 两级
|
||||
|
||||
| 级 | 存哪 | 容量 / 有效期 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| L1 | 各进程内存(api / worker / worker:agent 各一份) | 每个 region 最多 1000 条(LRU),5 分钟,且不超过条目在 L2 的有效期 | 只在跨进程失效监听在线时启用,见下 |
|
||||
| L2 | PG `kv_cache`(迁移 0042,0086 加 `fresh_until` / `pinned`) | 按 region 条数上限(TLRU,先删过期再删最久未用),每条独立 TTL | 三进程共享;UNLOGGED,PG 崩溃后清空 |
|
||||
|
||||
读:L1 → L2 → 源;写:两级都写。L1 返回的是副本,调用方改了不影响缓存。PG 读写失败按未命中 / 不写处理,不让业务请求失败。
|
||||
|
||||
### 策略表(来源 × 资源类型)
|
||||
|
||||
TTL 取值来自 `settings.storage.cacheTtl`(见 [storage.md](storage.md);旧库缺新字段时按缺省补),代码在 `metaCachePolicy`。
|
||||
|
||||
| 资源类型 | TMDB | 豆瓣 / Bangumi | 缺省 TTL | 空结果 / 查无 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 搜索 / 识别 | `/search/*`、`/find/*`、识别缓存 `tmdb.recognize`:`tmdbSearchDays` | 搜索请求:`metaSearchDays` | 7 天 | TMDB `tmdbMissDays`、豆瓣 / bgm `metaMissDays`,1 天 |
|
||||
| 详情 / 季 / 剧集组 | `/movie/{id}`、`/tv/{id}`、季、`/tv/episode_group/*`:`tmdbDetailDays` | 条目 `douban.subject` / `bangumi.subject`、发现页匹配 `discover.match`:`metaDetailDays` | 30 天 | 1 天(TMDB `tmdbMissDays`;条目不存在 `meta.subject_miss` 用 `metaMissDays`) |
|
||||
| 榜单 / 热门 / 每日放送 | trending / discover / popular 等:`tmdbListHours` | 榜单、标签、筛选、每日放送、趋势页:`metaListHours` | 6 小时 | TMDB `tmdbListEmptyHours`、豆瓣 / bgm `metaEmptyHours`,1 小时 |
|
||||
| 人物 / 图片 | `/person/*`、`…/images`:`imageDays` | — | 90 天 | `tmdbMissDays` |
|
||||
|
||||
- 空结果:TMDB 按 `results` 为空数组(MP `EMPTY_RESULT_CACHE_TTL` 判定);豆瓣 / bgm 按各接口的列表字段为空。空结果 TTL 不超过正常 TTL。
|
||||
- **失败不缓存**:网络错误、超时、鉴权失败、被拦(403、风控页)、限流、5xx 都在适配器里抛错,不写缓存(MP `skip_none` / `skip_if`)。
|
||||
豆瓣 / bgm 条目查询失败(不是「不存在」)时同一条目本进程冷却 30 分钟再在线查,不写缓存。
|
||||
- 条数上限:TMDB 两个 region 用 `cache.tmdbMaxSize`(缺省 5000);豆瓣 / bgm 请求 5000、条目 20000、发现匹配 20000。
|
||||
- `metaSearchDays` 缺省从 1 天改为 7 天(策略表统一);豆瓣 / bgm 搜索的空结果改按 `metaMissDays`(以前按 `metaEmptyHours`)。
|
||||
|
||||
### 过期先用旧值(stale-while-revalidate)
|
||||
|
||||
- 写入时 `fresh_until = now + TTL`、`expires_at = fresh_until + 宽限期`,宽限期 = TTL 的 50%,最多 7 天。
|
||||
- 过了 `fresh_until`、还没到 `expires_at` 的条目是旧值:先返回旧值,后台刷新;同一个 key 同时只刷一次。
|
||||
- 后台刷新失败继续用旧值,记一次警告「缓存后台刷新失败,继续用旧值」;过了宽限期就是普通未命中,同步回源。
|
||||
- `PgCache.get`(非元数据缓存与旧调用方用)只认新鲜条目;`PgCache.getEntry` 带回旧值,给 `MetadataCache` 用。
|
||||
- 识别缓存、豆瓣条目这类「只读不回源」的读取(`MetadataCache.get`)只认新鲜条目。
|
||||
|
||||
### 同请求合并(single-flight)
|
||||
|
||||
同进程同 region + key 并发只发一次请求,其余等同一个结果;后台刷新也走这条,刷新进行中的 key 不再发。跨进程不合并(各进程各自一次)。
|
||||
|
||||
### 关注中作品:pinned 与续期
|
||||
|
||||
- 关注口径同 `service/watched.ts isEntityWatched`(进行中的订阅、用户搜索 / 手动识别 / 人工确认、手动下载、在下载或整理、已在库)。
|
||||
集合 = 关注中作品的 TMDB 身份(`movie:<id>` / `tv:<id>`)+ 选中的剧集组(`group:<gid>`),按进程缓存 5 分钟。
|
||||
- 属于关注作品的 TMDB 请求(详情、季、剧集、图片、剧集组列表、剧集组详情)写入时标 `pinned`:按条数上限淘汰时跳过(仍计入条数,到期照样过期)。
|
||||
- L2 新鲜命中时续期:过期时间推到 now + 本类 TTL(只延长不缩短,保留宽限长度)。旧值不续期,该刷新就刷新。
|
||||
- 作品不再关注后 `pinned` 不会立即清掉,到期自然过期。
|
||||
|
||||
### 进入关注时预热
|
||||
|
||||
建订阅、用户搜索 / 手动识别出资源、提交下载时(`enqueueWatchedRefresh`,原来补排 full 刷新的触发点),每次额外入队一次
|
||||
`prewarmMetadata`(网络道,幂等键 `prewarmMetadata:<作品 id>:<日期>`,同一天重复进入只跑一次):
|
||||
|
||||
- 拉基础详情、富详情(含标志)、各季详情(按 `metadata.includeSpecials` 决定含不含季 0,最多 60 季)、选中的剧集组;
|
||||
请求键和刷新元数据 / 刮削完全相同,之后直接命中;预热前先清关注集合,保证这次写入就 pinned;
|
||||
- 海报、背景、标志的原图拉进图片缓存目录(刮削用的就是原图);已在磁盘上的不再下载;
|
||||
- 走 TMDB 客户端原有的限流与重试;详情都拿不到时网络类错误交给任务重试,其余记下结束。
|
||||
- 代码:`sources-identity/service/metadata-prewarm.ts`。
|
||||
|
||||
### 跨进程失效
|
||||
|
||||
- 每个进程启动时 `startMetadataCache` 开一条专用连接 `LISTEN umt_cache_invalidate`。
|
||||
- `PgCache.delete / clear`(含设置页「清空」、`POST /cache/tmdb/clear`、识别缓存类型冲突删除)和手动刷新(`fresh`)写入后
|
||||
`pg_notify('umt_cache_invalidate', {"r": region, "k": key})`;不带 `k` = 整个 region。各进程收到后丢掉对应 L1。
|
||||
- 普通写入不通知:别的进程 L1 里的旧值最多再用 5 分钟(L1 有效期)。
|
||||
- 监听在线才启用 L1;断开时立即停用并清空 L1,退避重连(最长 10 秒),重连成功后整体清一次再启用。
|
||||
启动时连不上只记警告,该进程只用 L2。脚本、测试等没启动监听的进程 L1 一直关着。
|
||||
|
||||
### 命中统计
|
||||
|
||||
- 每个进程按 region 在内存里计:L1 命中、L2 命中、旧值命中(先返回旧值)、未命中、回源失败、后台刷新。
|
||||
- 每分钟把增量加进表 `cache_stat`(迁移 0086,按「天 × region」一行;30 天前的行顺带删掉),进程退出时再写一次。
|
||||
- 口径:`GET /api/v1/cache/regions` 每个元数据 region 带 `hits`:最近 7 天 `cache_stat` 之和 + 当前进程还没写入的计数;
|
||||
别的进程最近不到一分钟的计数不在内。命中率 = (L1 + L2 + 旧值) / (L1 + L2 + 旧值 + 未命中),没读取过为 null。
|
||||
合并等待(single-flight 的跟随者)按未命中计。
|
||||
- 设置 → 缓存 → 缓存区表的「命中率」列显示百分比与明细(内存 / 数据库 / 旧值 / 未命中,有失败与后台刷新时附上);非元数据区显示「—」。
|
||||
- 排查:`select * from cache_stat where day = current_date order by region;`
|
||||
|
||||
### 接到这一层的 region
|
||||
|
||||
| region | 内容 | 键 | 读写方式 |
|
||||
|---|---|---|---|
|
||||
| `tmdb.request` | TMDB 请求响应(MP `TMDb.request`) | `path?query`(参数排序,不含凭证) | `fetch`:两级 + 旧值 + 合并 + pinned |
|
||||
| `tmdb.recognize` | TMDB 识别结果(MP `TmdbCache`) | `[类型][语言]{tmdbid 或名称}-{年份}-{季}` | `get` / `set`;未识别 `{id:0}` 按空结果 TTL |
|
||||
| `douban.request` / `bangumi.request` | 豆瓣 / bgm 搜索与列表请求 | `方法 地址?排序后的参数 [请求体]` | `fetch` |
|
||||
| `douban.subject` / `bangumi.subject` | 条目(标题 / 别名 / 年份) | 条目 id | 在线查走 `fetch`;站点详情页 / 网页摘要写入走 `set` |
|
||||
| `meta.subject_miss` | 豆瓣 / bgm 条目不存在 | `来源:id` | `set` / `get` |
|
||||
| `discover.match` | 豆瓣 / bgm 条目 → TMDB 作品的确定匹配 | `来源:id` | `get` / `set` |
|
||||
|
||||
其它 `kv_cache` 区(`site.detail`、`websearch`、`enrich*`、`identity.detail_ids`)仍直接用 `PgCache`,没有 L1 与命中统计:
|
||||
|
||||
| region | 内容 | 键 | TTL | 条数上限 |
|
||||
|---|---|---|---|---|
|
||||
| `tmdb.request` | TMDB 请求响应(MP `TMDb.request`) | `path?query`(不含凭证) | 搜索 / find:`storage.cacheTtl.tmdbSearchDays`(7 天);详情 / 季:`tmdbDetailDays`(30 天,追踪中的作品命中续期);`results` 为空:`tmdbMissDays`(1 天) | `cache.tmdbMaxSize` |
|
||||
| `tmdb.recognize` | TMDB 识别结果(MP `TmdbCache`) | `[类型][语言]{tmdbid 或名称}-{年份}-{季}` | `tmdbSearchDays`;未识别 `{id:0}` 按 `tmdbMissDays` | `cache.tmdbMaxSize` |
|
||||
| `site.detail` | 站点详情页扩展内容 | `source_release.id` | `max(站点详情缓存期, storage.cacheTtl.siteDetailDays)`(7 天) | 5000 |
|
||||
| `websearch` | 网页搜索结果 | 参数 sha256 | `storage.cacheTtl.webSearchDays`(1 天) | 1000 |
|
||||
|
||||
TTL 设置(CACHE-2 起):`settings.storage.cacheTtl`,见 [storage.md](storage.md)。MP 的 `META_CACHE_EXPIRE` 只有一个值,
|
||||
我们按请求类别拆成搜索 / 详情两档,`EMPTY_RESULT_CACHE_TTL` 对应 `tmdbMissDays`;缓存语义不变。
|
||||
条数上限仍是 `cache.tmdbMaxSize`(缺省 5000;MP `CONF.tmdb` 为 256,本项目缓存在 PG 里不占进程内存)。旧字段 `cache.tmdbTtlHours`、`cache.emptyResultTtlSec` 不再读取。
|
||||
### 清空与清理
|
||||
|
||||
API:`GET /api/v1/cache/tmdb?kind=recognize|request&limit=` 列出条目(键 / 过期 / 大小,识别条目带 tmdbId / 标题 / 海报);
|
||||
`POST /api/v1/cache/tmdb/clear` 清空两类(仅 admin)。
|
||||
- 设置 → 缓存 → 缓存区:按 region 清空(`POST /api/v1/cache/regions/{region}/clear`,仅 admin),同时通知各进程丢 L1。
|
||||
- `POST /api/v1/cache/tmdb/clear`:清 `tmdb.request` 与 `tmdb.recognize`(对应 MP `clear_cache()`)。
|
||||
- 定时任务 `cache.sweep`(维护组,每 10 分钟)物理删除已过硬过期(`expires_at`)的行;宽限期内的旧值不删。
|
||||
- 改了 TTL:新写入的条目按新值,已有条目保持原有效期,必要时清空。
|
||||
|
||||
API:`GET /api/v1/cache/tmdb?kind=recognize|request&limit=` 列出条目(键 / 过期 / 大小,识别条目带 tmdbId / 标题 / 海报)。
|
||||
|
||||
识别缓存何时读写(`sources-identity/service/tmdb-recognize-cache.ts`):
|
||||
- 识别资源时先读:命中且有标题 → 按 id 取 TMDB 详情作为唯一远端候选;命中 `{id:0}` → 不再搜索;强制复查(force)不读。
|
||||
@@ -48,20 +134,23 @@ API:`GET /api/v1/cache/tmdb?kind=recognize|request&limit=` 列出条目(键 /
|
||||
file 只允许 `[A-Za-z0-9_-]+.(jpg|jpeg|png|webp)`。
|
||||
- 缓存路径对照 MP `sanitize_url_path`:`DATA_DIR/cache/images/t/p/<size>/<file>`。
|
||||
- `cache.globalImageCache`(缺省关闭,MP `GLOBAL_IMAGE_CACHE`)开启才读磁盘缓存;拉到的合法图片总会写盘。
|
||||
刮削与关注作品预热取原图(`original`),总是先读磁盘缓存。
|
||||
- 按文件头识别图片类型,非图片 / SVG 拒绝;ETag = md5(内容),`Cache-Control: public, max-age=604800`,If-None-Match 相同回 304。
|
||||
- 上游超时(20s)/ 非 200 / 非图片 → 502,前端回退无彩占位。
|
||||
- 清理:定时任务 `cache.images_clean`(每天)删除 mtime 早于 `cache.globalImageCacheDays`(缺省 7)天的文件。
|
||||
- 容量 LRU(CACHE-2):`cache.images_lru`(30 分钟)在总量超过 `storage.budgets.imagesGb`(缺省 20 GB)时按最久未访问删到 90%;
|
||||
读盘命中会刷新文件 atime。见 [storage.md](storage.md)。
|
||||
- 用量 / 清空:`GET /api/v1/cache/images`、`POST /api/v1/cache/images/clear`(仅 admin);设置 → 缓存(缓存区表里的「图片」行可清空;「图片缓存」节开关与保留天数)。按区统计与清空通用缓存:`GET /api/v1/cache/regions`、`POST /api/v1/cache/regions/{region}/clear`(仅 admin),设置 → 缓存 → 缓存区。
|
||||
- 用量 / 清空:`GET /api/v1/cache/images`、`POST /api/v1/cache/images/clear`(仅 admin);设置 → 缓存(缓存区表里的「图片」行可清空;「图片缓存」节开关与保留天数)。
|
||||
|
||||
前端:作品库列表、作品详情、搜索面板的 TMDB 候选在有 `posterPath`(metadata.posterPath / TMDB 候选)时显示海报,
|
||||
加载失败回退原来的无彩占位。TMDB 详情刷新会把 posterPath / backdropPath 写进 metadata_field。
|
||||
加载失败回退原来的无彩占位。TMDB 详情刷新会把 posterPath / backdropPath / logoPath 写进 metadata_field。
|
||||
|
||||
## 常见问题
|
||||
|
||||
- TMDB 缓存不生效:确认 `select region, count(*) from kv_cache group by 1` 有 `tmdb.request`;PG 读写失败时按未命中处理并记
|
||||
`TMDB 缓存读写失败,按未命中处理`。
|
||||
`缓存读取失败,按未命中处理` / `缓存写入失败`。
|
||||
- 命中率一直是 0 或没有:`cache_stat` 有没有今天的行;进程日志里有没有 `缓存失效监听启动失败`(只影响 L1,L2 命中照样计)。
|
||||
- 清空后别的进程还拿到旧值:看该进程有没有 `缓存失效监听断开` 日志;断线期间 L1 是停用的,不会拿旧值。
|
||||
- 改了 TMDB 缓存设置:新写入的条目按新 TTL / 上限;已有条目保持原过期时间,必要时清空。
|
||||
- TTL 拉长后条目更早被条数上限挤掉:`cache.tmdbMaxSize` 缺省 5000,追踪作品很多时再调大。
|
||||
- TTL 拉长后条目更早被条数上限挤掉:`cache.tmdbMaxSize` 缺省 5000,关注作品的条目 pinned 不受影响,其余按最久未用淘汰。
|
||||
- 数据库重启后缓存为空:UNLOGGED 表的正常行为。
|
||||
+15
-5
@@ -26,8 +26,16 @@ PATCH /api/v1/settings {"storage":{"retentionDays":{"observation":60}}}
|
||||
| `retentionDays.capabilityStats` | 90 | 能力调用审计(`target_type=capability`)与已结束的 `capability_invocation` |
|
||||
| `retentionDays.identityPoolArchive` | 30 | 待识别池 unmatched 条目无新证据多少天后归档(不删) |
|
||||
| `cacheTtl.tmdbSearchDays` | 7 | TMDB `/search/*`、`/find/*` 响应与识别缓存 |
|
||||
| `cacheTtl.tmdbDetailDays` | 30 | TMDB 详情 / 季。追踪中的作品命中时续期 |
|
||||
| `cacheTtl.tmdbDetailDays` | 30 | TMDB 详情 / 季 / 剧集组。关注中的作品命中时续期 |
|
||||
| `cacheTtl.tmdbMissDays` | 1 | TMDB 空结果与未识别负缓存 |
|
||||
| `cacheTtl.tmdbListHours` | 6 | TMDB 榜单 / 热门 / 趋势 / 按条件筛选(小时) |
|
||||
| `cacheTtl.tmdbListEmptyHours` | 1 | TMDB 列表空结果(小时) |
|
||||
| `cacheTtl.imageDays` | 90 | TMDB 人物 / 图片列表(`/person/*`、`…/images`) |
|
||||
| `cacheTtl.metaSearchDays` | 7 | 豆瓣 / bgm 搜索请求(2026-10-06 起缺省 7 天,以前 1 天) |
|
||||
| `cacheTtl.metaDetailDays` | 30 | 豆瓣 / bgm 条目、发现页条目 → TMDB 匹配 |
|
||||
| `cacheTtl.metaMissDays` | 1 | 豆瓣 / bgm 搜索空结果、条目不存在 |
|
||||
| `cacheTtl.metaListHours` | 6 | 豆瓣 / bgm 榜单、热门、每日放送等列表(小时) |
|
||||
| `cacheTtl.metaEmptyHours` | 1 | 豆瓣 / bgm 列表空结果(小时) |
|
||||
| `cacheTtl.siteDetailDays` | 7 | 站点详情页扩展内容(`kv_cache` `site.detail`) |
|
||||
| `cacheTtl.webSearchDays` | 1 | 网页搜索结果 |
|
||||
|
||||
@@ -39,11 +47,13 @@ PATCH /api/v1/settings {"storage":{"retentionDays":{"observation":60}}}
|
||||
## 缓存有效期
|
||||
|
||||
只改 TTL 的取值来源,缓存语义还是对照 MP(见 [porting/moviepilot-tmdb-cache.md](../porting/moviepilot-tmdb-cache.md))。
|
||||
2026-10-06 起 TMDB / 豆瓣 / Bangumi 按「来源 × 资源类型」统一取值、过期先用旧值(宽限期 TTL 的 50%,最多 7 天),
|
||||
完整策略表、两级缓存与关注作品的 pinned / 续期见 [cache.md](cache.md)「统一元数据缓存」。
|
||||
|
||||
- TMDB 请求缓存:键以 `/search/` 或 `/find/` 开头的用搜索 TTL,其余用详情 TTL。`results` 为空的用未命中 TTL。
|
||||
- TMDB 请求缓存:键以 `/search/` 或 `/find/` 开头的用搜索 TTL;榜单类用列表 TTL;`/person/*`、`…/images` 用图片 TTL;其余用详情 TTL。`results` 为空的用未命中 TTL。
|
||||
- TMDB 识别缓存:正常条目用搜索 TTL(识别结果就是名称到条目的映射),负缓存 `{id:0}` 用未命中 TTL。
|
||||
- 追踪中的作品续期:作品有未结束的订阅(active / paused / pending)且有已接受的 TMDB 身份,
|
||||
它的详情键(`/movie/<id>?…`、`/tv/<id>…`)命中时把过期时间推到「现在 + 详情 TTL」,只延长不缩短。追踪集合每进程缓存 5 分钟。
|
||||
- 关注中的作品续期:口径同 `isEntityWatched`,它的详情 / 季 / 剧集组 / 图片键命中时把过期时间推到「现在 + 本类 TTL」,
|
||||
只延长不缩短,并标 pinned(条数上限淘汰时跳过)。关注集合每进程缓存 5 分钟。
|
||||
- 站点详情:`kv_cache` TTL 取 `max(站点详情缓存期, siteDetailDays)`。是否重抓仍按站点详情缓存期
|
||||
(`sites.defaultDetailCacheSec`,站点定义可更长)判断,下载前复核促销和 H&R 走的是这条。
|
||||
- 改了 TTL:新写入的条目按新值,已有条目保持原过期时间。
|
||||
@@ -71,7 +81,7 @@ PATCH /api/v1/settings {"storage":{"retentionDays":{"observation":60}}}
|
||||
| `storage.retention.identity_pool` | `identity_candidate` | 6 小时 | pending + unmatched,`coalesce(last_attempt_at, created_at)` 早于保留期 → 改为 archived |
|
||||
| `storage.analyze` | 全库 | 7 天 | `ANALYZE` |
|
||||
| `cache.images_lru` | 图片缓存 | 30 分钟 | 见上 |
|
||||
| `cache.sweep` | `kv_cache` | 10 分钟 | 已过期 |
|
||||
| `cache.sweep` | `kv_cache` | 10 分钟 | 已过硬过期(宽限期内的旧值保留) |
|
||||
|
||||
分批删除(`modules/system/retention.ts` 的 `deleteInBatches`):
|
||||
|
||||
|
||||
Reference in new issue
Block a user