Skip to content

fix: 为旧版本 profile 自动刷新增加生产级护栏#81

Draft
AsperforMias wants to merge 2 commits into
hikariming:mainfrom
AsperforMias:fix/stale-score-cache
Draft

fix: 为旧版本 profile 自动刷新增加生产级护栏#81
AsperforMias wants to merge 2 commits into
hikariming:mainfrom
AsperforMias:fix/stale-score-cache

Conversation

@AsperforMias

@AsperforMias AsperforMias commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

背景

旧版本 score_version 的历史评分在生产环境可能继续被用户页、榜单、搜索或 profile snapshot 命中,导致用户看到已经被新规则修复过的旧判断。

同时,旧版本 profile 的自动刷新路径如果缺少生产级护栏,会在版本切换、爬虫回访、sitemap/search 旧链接访问时放大成批量 scan 和 DB 写入压力。原实现主要按 username 做 cooldown;在 Redis 不可用或并发访问较多时,cooldown / single-flight 保护不足,存在请求风暴风险。

改动

  • 默认读取 scores / profile_snapshots 时只暴露当前 SCORE_CACHE_VERSION,旧版本行不再进入榜单、搜索、相似用户、开发者目录、百分位和排名。
  • 增加 includeStale 读取入口,仅用于识别旧缓存是否存在,避免公共事实面继续使用旧版本结果。
  • 用户访问旧 profile 时,服务端按需尝试执行一次确定性 scan + score,写入当前版本缓存和 profile snapshot。
  • 自动刷新不调用 writer/roast LLM,不生成毒舌文案,不烧 DeepSeek token。
  • 自动刷新复用 scan single-flight,并新增独立的旧 profile 刷新护栏:
    • Redis 不可用或缓存绕过时 fail-closed,不触发自动 scan。
    • per-IP 限流,避免单个来源批量刷新旧 profile。
    • 全局限流,避免版本切换后大量旧页面同时打爆后端。
    • per-user lease,避免同一用户并发刷新。
    • 刷新开始前写入 3 小时 cooldown,失败也不会短时间重复烧 GitHub scan 成本。
  • 刷新失败、被限流或处于 cooldown 期间,用户页会展示旧评分/旧报告作为临时兜底,但顶部明确标注“历史评分:规则版本已过期”,不伪装成当前规则结论。
  • 成功写库后删除临时 scan cache,避免 profile 页因为 live scan cache 误触发 LiveRoast
  • /api/score/[username] 遇到旧缓存时也会自动写回当前版本确定性分数。
  • 新增 recordDeterministicScan helper,明确区分“确定性评分写库”和“LLM roast 写文案”。
  • 将 Next 应用 tsconfig 排除独立 SDK package,避免网站生产构建被 packages/ghfind-js/tsup.config.ts 的子包构建依赖拖失败。

自检

  • ./node_modules/.bin/tsc --noEmit
  • ./node_modules/.bin/vitest run src/lib/__tests__/redis.test.ts src/lib/__tests__/db.test.ts
  • ./node_modules/.bin/eslint src/lib/redis.ts 'src/app/[locale]/u/[username]/page.tsx' src/lib/__tests__/redis.test.ts
  • git diff --check
  • 此前已验证旧版本缓存账号:自动刷新失败时仍展示旧分数/旧报告,同时显示历史评分提示;Light / Dark 主题均可读。

成本与生产风险

  • 不会全站预热,不会要求所有用户手动重测。
  • 只有访问旧 profile 或旧 /api/score 时才按需刷新该用户。
  • 该刷新只走 GitHub scan 和本地 deterministic scoring,不走 LLM。
  • Redis 不可用时不会触发自动 scan,直接展示旧 profile 兜底。
  • 成功后 DB 当前版本命中;失败、限流或异常时保留旧 profile,并通过 cooldown / lease / IP 限流 / 全局限流控制成本。

@AsperforMias
AsperforMias requested a review from hikariming as a code owner July 2, 2026 20:06
@vercel

vercel Bot commented Jul 2, 2026

Copy link
Copy Markdown

@AsperforMias is attempting to deploy a commit to the hikariming's projects Team on Vercel.

A member of the Team first needs to authorize it.

@AsperforMias
AsperforMias force-pushed the fix/stale-score-cache branch from 1c917fa to 85be1e9 Compare July 2, 2026 20:13
@AsperforMias
AsperforMias force-pushed the fix/stale-score-cache branch from 85be1e9 to 2836159 Compare July 3, 2026 07:57
@AsperforMias AsperforMias changed the title fix: 自动刷新旧版本评分缓存 fix: 为旧版本 profile 自动刷新增加生产级护栏 Jul 3, 2026
@AsperforMias

Copy link
Copy Markdown
Collaborator Author

补充说明本次追加的护栏修复:

这次把旧版本 profile 的“被动自动刷新”从无条件后台动作改成带生产护栏的按需刷新。

现在只有 Redis 可用、未触发 per-IP 限流、未触发全局限流、同一用户没有 lease/cooldown 时,才会执行确定性 scan + score。刷新开始前会先写入 3 小时 cooldown,因此即使 GitHub、DB 或后续写入失败,同一用户也不会在短时间内被重复扫描。

如果 Redis 不可用、缓存绕过、并发锁失败或限流命中,会 fail-closed:不触发自动 scan,直接展示旧 profile 兜底,并保留“历史评分:规则版本已过期”的提示。这样可以避免版本切换后 sitemap/search/爬虫批量访问旧 profile 时,把生产环境打成无节制 scan 和 DB 写入。

这次没有改评分公式、judge 规则或正常 /api/score 主链路,只收紧旧缓存被动刷新路径。

@AsperforMias

AsperforMias commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator Author

Owner review requested.

@AsperforMias
AsperforMias marked this pull request as draft July 17, 2026 19:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant