Skip to content

fix: 幂等缓存过期 + 限流桶清理 + 模型列表按分组过滤 - #8

Merged
mzrodyu merged 1 commit into
mainfrom
feat/model-pull-button
Sep 10, 2026
Merged

mzrodyu merged 1 commit into
mainfrom
feat/model-pull-button

Conversation

@mzrodyu

@mzrodyu mzrodyu commented Sep 10, 2026

Copy link
Copy Markdown
Owner

复查分组功能时发现的三个缺陷。

1. idempotencyCache 永不过期

CachedResponse.CreatedAt 被写入但从未被读取(全仓库只有 3 处读取、2 处写入,无任何 TTL 比较)。后果:

  • 同一个 Idempotency-Key 重放会永久返回当初的响应体,而不是「多久之内」
  • 缓存保存完整响应体且无上限,只有重启/restore 才清

→ 增加 idempotencyCacheTTL(24h),读取时剔除过期项并在 pruneExpiredIdempotencyLocked 中回收。

2. rateLimitBuckets 永不清理

bucket key 为 keyID:unixMinute,每个 key 每个活跃分钟留一条,进程生命周期内单调增长。pruneOperationalHistoryLocked 只覆盖 logs 与 quota ledger。

→ 每分钟首次请求时顺带清理非当前分钟的桶;pruneOperationalHistoryLocked 中也会清理(两处均不标记 changed,因为这些是纯内存状态,不应触发落盘)。

3. 模型列表未按分组过滤

/v1/models/api/catalog/models 只按 status == "available"(及 key 的 allowedModels)过滤,完全忽略分组。模型只挂在受限渠道上时,用户看得到却在调用时收到 model_not_available

→ 已登录调用方只看到自己分组实际可路由到的模型;匿名访客仍看到完整目录

新增的 channelServesModelForGroupLocked 刻意不复用 channelCandidatesLocked——后者会展开 key 池并逐个解密密钥,按模型遍历目录时代价过高。

验证

新增 4 个测试,全部通过:

  • TestIdempotencyCacheExpires —— 过期/时间戳非法条目被拒绝并清除,新鲜条目保留
  • TestRateLimitBucketsAreSweptPerMinute —— 陈旧桶被清、当前桶保留且计数正确
  • TestOpenAIModelListHidesModelsTheGroupCannotReach —— 受限模型消失,未受限模型保留
  • TestPublicCatalogFiltersBySessionGroup —— 匿名可见全部;登录后按分组过滤

go test ./... 全绿。注意:未能运行 -race,本机缺少 gcc/cgo(cgo: C compiler "gcc" not found),并发改动仅经人工审查。

…e model list

Three defects found while reviewing the group work:

- idempotencyCache never expired. CachedResponse.CreatedAt was written but
  never read, so a reused Idempotency-Key replayed its original body forever
  and the cache (holding whole response bodies) grew without bound.
- rateLimitBuckets accumulated one entry per key per active minute for the
  lifetime of the process; pruneOperationalHistoryLocked only covered logs and
  the quota ledger.
- /v1/models and /api/catalog/models ignored group visibility, advertising
  models whose only channels the caller's group cannot reach. A user would see
  the model and then get model_not_available on the call.

The catalog stays fully visible to anonymous visitors; signed-in callers see
only what their group can route to. The new check avoids channelCandidatesLocked
because that expands key pools and decrypts secrets per model.

Also adds a race-detector note: it needs cgo, which is unavailable on this box.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@mzrodyu
mzrodyu merged commit 9b4cd9e into main Sep 10, 2026
1 check passed
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