Files

92 lines
2.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 客户端缓存键作用域规范(Cache Key Scoping
## 目标
防止同一设备多账号切换时出现缓存串读(A 账号数据在 B 账号展示)。
本规范适用于 `apps/lib/data/cache/**` 以及所有通过 `CachedRepository` 读写的业务缓存。
---
## 作用域模型
### 1) 作用域级别
当前实现仅启用以下作用域:
- `user:<user_id>`:已登录用户态
未登录态实现说明:
- `apps/lib/app/services/session_scope_manager.dart` 在登出后会调用 `CacheScope.resetProvider()`
- 当 provider 未配置时,`apps/lib/data/cache/cached_repository.dart` 会跳过 scoped cache 读写,而不是写入 `anonymous` 命名空间。
### 2) 键格式
客户端持久缓存最终键必须按以下格式生成:
`cache:<scope>:<feature-key>`
当前实现未使用会话代际后缀(epoch/version token),最终键格式为:
- `cache:user:<user_id>:<feature-key>`
示例:
- `cache:user:8ef4...:chat:history:first:default`
- `cache:user:8ef4...:calendar:day:2026-03-29`
---
## 责任边界
### 基础层(必须)
- `CachedRepository` 负责统一附加 `<scope>` 前缀。
- Feature Repository 只声明业务键(`<feature-key>`),不得手工拼接 userId 前缀。
### 应用层(必须)
- 在认证状态变化时更新当前缓存作用域:
- 登录成功 -> `user:<user_id>`
- 登出/失效 -> 清空当前用户作用域并重置 provider(不进入 `anonymous` 作用域)
---
## 并发与切号安全
- 切号后,旧账号异步请求结果不得回写到新账号 UI 状态。
- 当前实现依赖 `user:<user_id>` 级别缓存分区 + 认证切换时清理上一用户前缀:
- - 登录切换:清理上一用户 `cache:user:<old_user_id>:` 前缀
- - 登出:清理当前用户前缀并关闭 scoped cache 读写
- 当前实现未引入 epoch/token 代际保护;若未来出现“切号后旧请求晚到并覆盖新 UI”问题,需要另行升级协议与实现。
---
## 兼容与迁移策略
### 向后兼容
- 旧无作用域键允许保留在本地存储中,不参与新读取路径。
- 新版本只读取带 `cache:user:<user_id>:` 前缀的键。
### 迁移方式
- 采用增量迁移(additive),不执行强制删除旧键。
- 如需清理旧键,必须通过统一维护任务处理,不在功能逻辑中零散实现。
---
## 验收标准
1. 同设备 A/B 账号来回切换,不出现跨账号历史/列表串读。
2. 登录后首次读取命中当前用户作用域键;登出后 scoped cache 不再参与读取。
3. Feature 仓库不再自行实现 userId 拼 key 逻辑。
---
## Compatibility Strategy
- 策略:`backward-compatible`
- 本次为文档对齐更新,收敛未落地的 `anonymous`/epoch 设计,不改变现有应用行为。