4.7 KiB
数据库存储审计与优化计划
结论
当前数据库设计的主体边界是清晰的:账号密钥、cookie、quota 缓存、事件日志、管理会话和模型快照都有独立表承载。账号调度依赖的窗口用量和 session affinity 已从纯内存状态收敛到 SQLite,重启后的短期调度退化已经被消除。事件日志写入也已统一到 LogService,请求链路会遵守运行时日志策略。
最优先要修复的是账号用量窗口状态。以下字段属于 account_usage,不是 accounts:
window_request_countwindow_input_tokenswindow_output_tokenswindow_cached_tokenswindow_started_atwindow_reset_atlimit_window_seconds
原因是它们描述的是账号在当前 rate-limit window 内的用量快照,而不是账号身份、密钥或生命周期属性。放在 account_usage 可以让累计统计和窗口统计共享同一个存储边界,也能让 least_used 调度在重启后恢复更多上下文。
已验证问题
Account 和 AccountPool 已经有窗口字段,调度逻辑也会使用 window_reset_at、窗口内请求数和 token 计数。审计时 account_usage 表没有这些列,AccountRepository::list_pool_accounts() 也把窗口字段固定恢复为 0 或 None。当前实现已将这些字段落到 account_usage,并在旧库启动时自动补列。
这不会损坏 access token、refresh token 或 cookie,也不会造成永久性数据异常。真实影响是:
- 重启后窗口计数归零,
least_used会丢失短期负载信息。 window_reset_at丢失后,调度无法按上游 rate-limit window 做更精确排序。- 窗口内 token 消耗无法跨进程保留,统计和排障信息不完整。
Claude 审计中“窗口字段未持久化”的结论成立,但“配额控制失效”的表述偏重。项目已经持久化 quota_json、quota_limit_reached、quota_cooldown_until 和 Cloudflare cooldown;缺失的是窗口统计快照,而不是所有配额状态。
目标存储边界
accounts 继续只承载账号身份、密钥密文、状态和上游 quota/cooldown 摘要。
account_usage 承载所有本地统计:
- 累计请求和 token 计数。
- 空响应计数。
- 当前窗口请求和 token 计数。
- 当前窗口开始、重置时间和窗口长度。
session_affinities 承载 previous_response_id 到账号和会话上下文的映射。当前实现已补 SessionAffinityRepository,完成响应时同步写库,启动时清理过期记录并恢复未过期映射。表结构补充 function_call_ids_json,避免工具调用分支信息只存在于内存。
事件日志通过统一日志服务入口写入,避免请求链路绕过日志容量、开关和 body capture 策略。
优化阶段
P0:持久化 account_usage 窗口状态(已落地)
- 扩展
account_usageschema。 - 对旧库启动时自动补列。
record_usage同时更新累计计数和窗口计数。list_pool_accounts从数据库恢复窗口字段。- 收到 rate-limit header 时持久化
window_reset_at和limit_window_seconds。
P1:持久化 session affinity(已落地)
- 增加
SessionAffinityRepository。 - 将内存 affinity map 的写入同步到
session_affinities。 - 启动时清理过期记录并恢复未过期映射。
- 补充
function_call_ids_json,恢复function_call_ids。
P1:统一事件日志入口(已落地)
- v1 response 和 401 后账户刷新失败事件通过
LogService::record写入。 - 在一个入口执行
enabled、capacity、capture_body和 metadata 规范。 - 管理端
/admin/logs/state和请求链路共享同一个LogService实例,运行时开关立即生效。
P2:收敛 schema 约束(已落地)
- 为账号状态、事件日志级别增加枚举 check constraints。
- 为 enabled、quota_limit_reached 增加 0/1 check constraints。
- 为 usage 计数、窗口计数、latency、session input_tokens 增加非负 check constraints。
- 为
limit_window_seconds增加正数约束。
P2:收敛直接 SQL(已落地)
admin_users和admin_sessions已收敛到AdminAuthRepository。- 管理登录、session 校验、默认管理员初始化和过期 session 清理都通过 repository 访问数据库。
- fingerprint 更新已从 updater 内部 SQL 迁移到
FingerprintRepository。 fingerprints同时支持历史记录写入和auto_updated当前记录 upsert。
P2:规范 cookie expires_at 读取语义(已落地)
CookieRepository::cookie_header读取时过滤已过期 cookie。- 支持解析标准
Expires时间和 RFC3339 时间。 - 无法解析的历史值保守保留,避免误删仍可能有效的 cookie。