Back to Chatbox

存储架构文档

docs/storage.md

1.20.15.3 KB
Original Source

存储架构文档

Chatbox 跨平台存储方案和版本迁移机制说明。

跨平台存储方案

存储类型

  • DESKTOP_FILE: 桌面端文件存储(通过 IPC)
  • INDEXEDDB: IndexedDB(通过 localforage)
  • LOCAL_STORAGE: localStorage(已弃用)
  • MOBILE_SQLITE: SQLite 数据库(通过 Capacitor)

当前方案(v1.17.0)

平台Settings/ConfigsSessions原因
Desktop文件存储IndexedDB配置便于备份,会话需要大容量
MobileSQLiteSQLite统一存储,性能更好
WebIndexedDBIndexedDB大容量,异步访问

版本历史

版本Config VersionDesktopMobile主要变化
v1.9.8-v1.9.100-5全部 FilelocalStorage初始版本
v1.9.116-7-→ SQLiteMobile 解决容量限制
v1.12.07-8--数据格式:sessions → session-list
v1.13.19-10--Provider/Session 设置重构
v1.16.111-12Sessions → IndexedDB
Configs 保持 File→ IndexedDBDesktop 分离存储
Mobile 统一到 IndexedDB
v1.17.012-13Sessions 保持 IndexedDB
Configs 保持 File→ SQLiteDesktop 无变化
Mobile 性能优化

关键历史事实

  • Desktop 的 configVersion/settings/configs 从未 存储在 IndexedDB 中
  • Desktop 从 v1.16.1 开始只将 sessions 移到 IndexedDB
  • v1.16.1 → v1.17.0,Desktop 存储策略 完全未变
  • Mobile 的完整演进:localStorage → SQLite (v1.9.11) → IndexedDB (v1.16.1) → SQLite (v1.17.0)

迁移机制

核心逻辑

typescript
// 1. 找到最新的旧存储
const [oldConfigVersion, oldStorage] = await findNewestStorage(getOldVersionStorages())

// 2. 判断是否需要迁移数据
if (
  (oldConfigVersion > configVersion || platform.type === 'desktop') &&
  oldStorage &&
  oldStorage.getStorageType() !== storage.getStorageType()  // 存储类型不同
) {
  await doMigrateStorage(oldStorage)  // 迁移数据
}

// 3. 增量升级数据格式
for (; configVersion < CurrentVersion; configVersion++) {
  await migrateFunctions[configVersion]?.(dataStore)
  await setConfigVersion(configVersion + 1)
}

迁移策略差异

平台策略说明
Mobile复制所有 key所有数据在同一存储
Desktop只复制会话数据Settings/Configs 保留在文件中

关键设计决策

1. 同类型存储共享数据源

原则: 旧存储和当前存储类型相同时,无需迁移数据。

示例: Mobile v1.9.11 (SQLite v7) → v1.17.0 (SQLite v13)

  • 都用 SQLite,数据已经在那里
  • 只需升级数据格式,无需复制数据

2. 多个旧存储选最新

原则: 存在多个旧存储时,选择 configVersion 最大的。

示例: localStorage v5 + IndexedDB v12 → 选择 IndexedDB v12

  • 避免迁移过时数据
  • 确保用户获得最新状态

3. 桌面端混合存储

原则: 配置文件便于备份,会话数据用 IndexedDB。

历史演进:

  • v1.9.x: 所有数据在 config.json 文件中
  • v1.16.1: 会话数据移到 IndexedDB,配置保持在文件中
  • v1.17.0: 与 v1.16.1 完全相同(无变化)

关键事实:

  • configVersion/settings/configs 从未在 IndexedDB 中存储过
  • 只有会话数据(chat-sessions-listsession:*)在 IndexedDB

特殊处理:

  • 迁移时只复制会话数据到 IndexedDB
  • Settings/Configs 保留在文件存储

4. 增量数据格式升级

原则: 数据格式升级按版本逐步执行。

优势:

  • 从任意版本升级都能正确迁移
  • 中断后可继续
  • 便于维护

测试要点

覆盖场景

  1. ✅ 首次运行(无旧数据)
  2. ✅ 版本已是最新(跳过迁移)
  3. ✅ 同类型存储(数据已可访问)
  4. ✅ 跨存储迁移(File → IndexedDB, localStorage → SQLite)
  5. ✅ 多个旧存储共存(选择最新版本)
  6. ✅ 历史版本直接升级(跳过中间版本)

关键洞察

1. 同类型存储共享数据源

typescript
// 测试 mock 体现
if (type === 'MOBILE_SQLITE') {
  sqliteData = { ...data }  // 共享同一数据容器
}

2. 最新版本优先

typescript
// localStorage v5 + IndexedDB v12 → 选择 v12

3. 桌面端部分迁移

typescript
// 只迁移 sessions,不迁移 settings/configs

4. 使用真实 Platform 实例

typescript
beforeAll(async () => {
  const { default: DesktopPlatformClass } = await import('@/platform/desktop_platform')
  desktopPlatform = new DesktopPlatformClass(window.electronAPI)
})

常见问题

Q: 为什么回退到 SQLite?
A: IndexedDB 在某些 WebView 环境存在数据被清理问题,SQLite 更稳定。

Q: 迁移失败会怎样?
A: 捕获异常并记录,应用仍可运行(初始化默认数据)。

Q: 如何添加新版本?
A: 增加 CurrentVersion,在 migrateFunctions 添加迁移函数,更新文档。

参考


最后更新: 2025-10-25 | 当前版本: v1.17.0 (Config Version 13)