问题概述
在当前实现中,运行时统计与部分持久化表对“账户标识(auth_id / quota_cache id / routing cursor)”的持久化依赖于凭证文件名或由它派生的值。结果是:当用户删除凭证并以不同文件名重新导入同一账号时,历史数据(运行时统计、配额缓存、路由游标、使用日志等)无法与新导入的账号重新关联,从而造成历史记录“断裂”。
复现步骤
问题主要出现在账户订阅升级或降级,重新oauth2认证会生成不同的文件名(-plus、-pro)
- 在管理界面或文件系统中添加一个凭证文件(例如 my-account.json)并让系统加载。
- 生成一些请求、配额/监控写入(会产生 auth_runtime_stats、quota_cache、routing_cursor_state、usage_events 等记录)。
- 删除该凭证文件并以不同的文件名(例如 my-account-2.json)重新导入相同账号的凭证内容。
- 发现历史记录没有关联到新导入的账号(UI 与查询结果表现为历史数据丢失或重置)。
受影响范围(关键表/字段)
- auth_runtime_stats.auth_id — 当前使用 auth.ID(由文件名派生)作为写入值,导入/重命名后无法匹配旧记录。
- auth_runtime_stats.file_name — 存放文件名 basename(用于追溯,但不能当做稳定标识)。
- quota_cache.id / quota_cache.file_name — quota_cache 的 id 与 file_name 也含文件名/索引信息,导入后断裂。
- routing_cursor_state.last_auth_id — 轮询游标记录 last_auth_id,若为文件名派生则会丢失游标位置。
- usage_events.auth_index — 使用 auth.Index(位置索引),账户顺序变化会影响关联。
根本原因
- auth_runtime_stats 等模块在写入/恢复时以 auth.ID(当前由文件名或与文件名紧密耦合的值)作为主查找键或恢复回退键。
- authRuntimeIdentityFingerprint 的实现(见 cliproxyapi-pro-core/patches/auth_runtime_state.go)对指纹的计算依赖 provider、文件名 basename 以及少量 metadata 字段(email、account_id、subject、user_id 等);当 metadata 缺失时指纹退化为依赖文件名(甚至回退到按位置索引 auth.Index),因此文件名变化导致指纹不匹配并在恢复时直接丢弃旧条目。
- 不同表之间的匹配策略与指纹算法不一致(例如 quota_cache 的指纹/ID 生成与 auth_runtime_stats 不统一),导致跨表重关联困难。
关键代码位置(参考)
- authRuntimeIdentityFingerprint: cliproxyapi-pro-core/patches/auth_runtime_state.go (指纹计算)
- authRuntimeStatsSnapshot: cliproxyapi-pro-core/patches/auth_runtime_state.go (构造写入 snapshot 时使用 auth.ID 作为 AuthID)
- applyStoredAuthRuntimeStats: cliproxyapi-pro-core/patches/auth_runtime_state.go (恢复时使用指纹校验并在不匹配时拒绝应用)
- store schema 与表定义: cliproxyapi-pro-core/patches/sources/internal/pro/observability/store.go(usage_events、auth_runtime_stats、quota_cache、routing_cursor_state)
问题概述
在当前实现中,运行时统计与部分持久化表对“账户标识(auth_id / quota_cache id / routing cursor)”的持久化依赖于凭证文件名或由它派生的值。结果是:当用户删除凭证并以不同文件名重新导入同一账号时,历史数据(运行时统计、配额缓存、路由游标、使用日志等)无法与新导入的账号重新关联,从而造成历史记录“断裂”。
复现步骤
问题主要出现在账户订阅升级或降级,重新oauth2认证会生成不同的文件名(-plus、-pro)
受影响范围(关键表/字段)
根本原因
关键代码位置(参考)