Skip to content

数据存储层 ​

本文引用的源码文件

本文引用的文件

目录 ​

  1. 引言
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与一致性
  8. 故障排查指南
  9. 结论
  10. 附录:数据模型与操作示例

引言 ​

本文件聚焦扩展端的数据存储层,围绕 IndexedDB 封装、会话持久化、ReAct 检查点双写策略、Token 统计聚合以及配置管理中的用户设置合并与迁移展开。文档目标包括:

  • 解释数据库连接管理、事务处理、版本升级与 store 缺失恢复机制。
  • 说明会话状态持久化、切换流程、归档/恢复、分叉复制与定时任务宿主会话。
  • 描述 ReAct checkpoint 的 IndexedDB + chrome.storage.local 双写与降级读取策略。
  • 梳理 Token 使用统计的索引优化、时间范围查询与多维聚合。
  • 总结配置管理系统中默认值合并、导入策略、扁平键投影与版本迁移。
  • 给出数据模型设计、索引策略、约束规则与常见 CRUD、批量、备份恢复示例路径。

项目结构 ​

数据存储相关代码集中在 src/storage,并由 background、side panel、options 等模块调用:

  • db.js:IndexedDB 封装层,定义数据库名、版本号、objectStore、索引、通用事务包装器、CRUD、迁移与 ReAct checkpoint 双写。
  • session-store.js:会话存储适配器,提供与旧 API 兼容的会话加载、保存、创建、切换、归档、恢复、复制、删除、重命名、排序等能力。
  • token-store.js:Token 使用统计的 IndexedDB CRUD 与聚合查询接口。
  • 上层调用方:background 的 ReAct 循环写入 checkpoint;options 的配置导入与厂商配置合并;side panel 的收藏、会话 UI 等。
graph TB
subgraph "扩展端"
SW["Service Worker / Background"]
Panel["Side Panel"]
Options["Options 页面"]
end
subgraph "存储层"
DB["IndexedDB 封装<br/>db.js"]
SS["会话存储适配器<br/>session-store.js"]
TS["Token 统计存储<br/>token-store.js"]
end
subgraph "浏览器存储"
IDB["IndexedDB"]
CS["chrome.storage.local"]
end
SW --> DB
Panel --> SS
Options --> DB
SS --> DB
TS --> DB
DB --> IDB
DB -.-> CS

图表来源

章节来源

核心组件 ​

  • IndexedDB 封装层(db.js)
    • 单例连接、onversionchange/onblocked 处理、store 存在性检测与自动重试。
    • withStore 统一事务包装,支持 readwrite/readonly、失败重试、连接重置。
    • sessions、activeSession、archivedSessions、uiPrototypes、tokenStats、reactCheckpoints、bookmarks、scheduledTasks 等对象存储及索引。
    • 从 chrome.storage.local 到 IndexedDB 的迁移工具。
    • ReAct checkpoint 的双写(先 chrome.storage.local,再 IndexedDB)与降级读取。
  • 会话存储适配器(session-store.js)
    • 初始化时触发 ensureMigration,清理孤儿 scrollPosition key。
    • loadSessions/saveSessions 并发读写活跃会话与活跃会话标记。
    • saveCurrentSession 将当前 state 映射为会话对象并持久化。
    • createSession/importSessions/appendMessageToSession/duplicateSession/archiveCurrentSession/restoreArchivedSession/renameSession/reorderSessions/createScheduledSession 等完整生命周期方法。
    • deleteStoreSession 同步清理 bookmark、scrollPosition、checkpoint 并就近激活相邻会话。
  • Token 统计存储(token-store.js)
    • recordTokenCall 记录单次 API 调用的 token 用量、上下文窗口占用率、调用类型等。
    • getSessionTokenSummary 利用 sessionId 索引 + KeyRange 缩小扫描范围。
    • getOverallTokenSummary/getDailyTokenSummary/getOverallTokenSummaryByRange/getTokenSummaryByModel/getTokenSummaryByCallType 等多维聚合。
    • clearSessionTokenStats/clearAllTokenStats 清理接口。

章节来源

架构总览 ​

数据流从业务层进入存储层,最终落盘到 IndexedDB,必要时回退到 chrome.storage.local。关键路径包括:

  • 会话读写:通过 session-store.js 调用 db.js 的 withStore,保证事务提交后再返回结果。
  • ReAct checkpoint:background/react-loop.js 调用 db.js 的 saveReactCheckpoint,采用“可靠优先”的双写策略。
  • Token 统计:业务侧记录调用后由 token-store.js 写入 tokenStats,并提供多种聚合查询。
  • 配置管理:options/config-io.js 与 shared/model-profiles.js 负责厂商配置合并、扁平键投影与迁移。
sequenceDiagram
participant BG as "Background(ReAct)"
participant DB as "db.js"
participant IDB as "IndexedDB"
participant CS as "chrome.storage.local"
BG->>DB : saveReactCheckpoint(checkpoint)
DB->>CS : set(key, data)
DB->>IDB : put reactCheckpoints
IDB-->>DB : success/failure
CS-->>DB : ok
DB-->>BG : boolean (是否至少一个存储成功)

图表来源

详细组件分析 ​

IndexedDB 封装层(db.js) ​

  • 数据库连接管理
    • 单例缓存 dbPromise,避免重复打开。
    • onversionchange 主动关闭当前连接并清空缓存,允许其他连接进行版本升级。
    • onblocked 不直接 reject,等待后续重试获取新连接。
  • 事务处理
    • withStore 封装事务创建、回调执行、complete/error/abort 事件处理。
    • 当 store 不存在或事务异常时,自动 resetDBConnection 并重试一次,防止 Service Worker 重启导致连接失效。
  • 数据迁移
    • migrateFromChromeStorage 将旧 sessions/conversationSessions 迁移至 IndexedDB,并清理大数据 key。
    • ensureMigration 在首次访问时判断是否需要迁移。
  • ReAct Checkpoint 双写
    • saveReactCheckpoint 先写 chrome.storage.local,再尝试 IndexedDB;任一成功即认为成功。
    • getReactCheckpoint 优先读 IndexedDB,失败降级到 chrome.storage.local。
    • getAllReactCheckpoints 合并两个来源去重,sessionId 作为主键。
    • cleanupExpiredReactCheckpoints 基于 TTL(7 天)清理过期 checkpoint。
flowchart TD
Start(["withStore(storeName, mode, callback)"]) --> GetDB["getDB() 获取连接"]
GetDB --> CheckStore{"store 是否存在?"}
CheckStore --> |否| CloseAndRetry["关闭连接并重置缓存<br/>延迟重试一次"]
CheckStore --> |是| CreateTx["创建事务"]
CreateTx --> ExecCb["执行回调并保存结果"]
ExecCb --> TxComplete{"事务完成?"}
TxComplete --> |是| Resolve["resolve(结果)"]
TxComplete --> |否| HandleError["error/abort 分支"]
HandleError --> RetryOrReject{"是否已重试?"}
RetryOrReject --> |否| ResetAndRetry["resetDBConnection()<br/>延迟重试一次"]
RetryOrReject --> |是| Reject["reject(错误)"]

图表来源

章节来源

会话存储适配器(session-store.js) ​

  • 初始化与迁移
    • init 调用 idb.ensureMigration,确保 IndexedDB 有数据或完成迁移。
    • cleanupOrphanScrollPositions 清理无对应会话的 scrollPosition_* key。
  • 会话加载与保存
    • loadSessions 并发获取 sessions 列表与 activeSessionId,按 order/createdAt 排序。
    • saveSessions 并发写入 sessions 列表与 activeSessionId。
  • 当前会话持久化
    • saveCurrentSession 将 state 中的 model、tools、agentId、temperature、topP、contextCompaction、messageHistory 等映射为会话对象,更新 title、updatedAt,并持久化。
  • 会话生命周期
    • createSession:生成唯一 ID,写入默认字段,设为活跃会话。
    • importSessions:导入多条会话,重新生成 ID/order,必要时自动切换到首个导入会话。
    • appendMessageToSession:向指定会话追加消息,适用于后台任务仍在执行时的异步保存。
    • switchToSession:并发保存当前会话与读取目标会话,更新 activeSessionId、state 字段,并根据 agentId 决定应用 Agent 配置或全局配置;恢复 isGenerating 状态需校验 pendingCallApiSessionIds。
    • deleteStoreSession:删除会话、清理 scrollPosition、checkpoint、bookmark,若删除的是活跃会话则就近激活相邻会话。
    • renameSession/reorderSessions:重命名与批量更新 order。
    • archiveCurrentSession:构造归档会话(最多保留 20 条),替换归档列表,删除原活跃会话并创建空会话。
    • restoreArchivedSession:从归档恢复为新活跃会话,并从归档移除。
    • duplicateSession:支持完整复制与消息级分叉(upToMessageId),深拷贝 executionLog/contextBubbles,重新生成 messageId,记录 forkMetadata。
    • createScheduledSession:为定时任务创建宿主会话,标记 scheduledTaskId,避免覆盖全局默认值。
sequenceDiagram
participant UI as "UI/业务层"
participant SS as "session-store.js"
participant DB as "db.js"
participant CS as "chrome.storage.local"
UI->>SS : switchToSession(targetId)
SS->>SS : saveCurrentSession()
SS->>DB : getSession(targetId)
DB-->>SS : targetSession
SS->>DB : setActiveSessionId(targetId)
SS->>CS : get(['modelName','temperature','topP'])
alt 绑定自定义 Agent
SS->>SS : 从 Agent 配置加载 model/temperature/topP
else 默认 Agent
SS->>SS : 应用全局 modelName/temperature/topP
end
SS-->>UI : targetSession

图表来源

章节来源

Token 统计存储(token-store.js) ​

  • 记录与聚合
    • recordTokenCall 写入 tokenStats,包含 sessionId、model、prompt/completion/totalTokens、contextWindow、contextUsageRate、callType、timestamp。
    • getSessionTokenSummary 使用 sessionId 索引 + KeyRange 缩小候选集,计算总量、调用次数、平均/最大/最小上下文占用率,并返回最近 20 条记录。
    • getOverallTokenSummary 计算全局总量、API 调用次数、会话数(排除系统会话)、最后更新时间。
    • getDailyTokenSummary 按天聚合,使用 timestamp 索引下界过滤。
    • getOverallTokenSummaryByRange 按时间范围过滤并聚合。
    • getTokenSummaryByModel/getTokenSummaryByCallType 按模型/调用类型聚合百分比。
  • 清理
    • clearSessionTokenStats/clearAllTokenStats 提供会话级与全局清理。
flowchart TD
Record["recordTokenCall(record)"] --> Put["withStore('tokenStats', 'readwrite')"]
Put --> Done["返回 true/false"]
Query["getSessionTokenSummary(sessionId)"] --> Index["index('sessionId').getAll(KeyRange)"]
Index --> Filter["精确过滤 sessionId 与子任务前缀"]
Filter --> Aggregate["计算总量/次数/占用率/Top20"]
Aggregate --> Result["返回汇总对象"]

图表来源

章节来源

配置管理系统(用户设置、默认值合并、验证与版本迁移) ​

  • 导入策略与合并
    • options/config-io.js 支持 merge/replace 两种导入策略。
    • merge 模式下对 modelProfiles 按 ID 对齐合并:新 ID 追加,同 ID 以 updatedAt 较新的覆盖,旧的忽略;之后统一重投影扁平键(apiBase/apiKey/modelName/customModels)以保证“扁平键 = 激活配置”。
    • replace 模式完全替换,并对密钥进行混淆后写入。
  • 厂商配置迁移与一致性
    • shared/model-profiles.js 提供 ensureProfilesMigrated,将旧扁平键迁移为 profiles 数组,并保证 activeProfileId 有效。
    • applyProfile/deleteProfile 等操作会同步扁平键,保持新旧格式一致。
    • mergeProfiles 实现按 ID 对齐合并逻辑,空白默认配置可被导入条目覆盖。
flowchart TD
Import["导入配置"] --> Strategy{"策略: merge | replace"}
Strategy --> |merge| MergeProfiles["mergeProfiles(local, incoming)"]
MergeProfiles --> ProjectFlat["重投影扁平键(apiBase/apiKey/modelName/customModels)"]
Strategy --> |replace| EncodeSecrets["_encodeConfigSecrets(config)"]
ProjectFlat --> Persist["chrome.storage.local.set(config)"]
EncodeSecrets --> Persist
Persist --> EnsureProfiles["ensureProfilesMigrated()"]
EnsureProfiles --> ValidateActive["确保 activeProfileId 有效"]

图表来源

章节来源

依赖关系分析 ​

  • db.js 是底层依赖,被 session-store.js、token-store.js 直接引用。
  • session-store.js 依赖 side_panel/state.js 与 i18n,同时与 chrome.storage.local 交互(scrollPosition、全局模型配置)。
  • token-store.js 仅依赖 db.js 的 getDB/withStore。
  • background/react-loop.js 依赖 db.js 的 ReAct checkpoint 接口,并在写入后进行读回验证。
graph LR
RL["background/react-loop.js"] --> DB["storage/db.js"]
SS["storage/session-store.js"] --> DB
TS["storage/token-store.js"] --> DB
SS --> STATE["side_panel/state.js"]
SS --> CS["chrome.storage.local"]

图表来源

章节来源

性能与一致性 ​

  • 连接与事务
    • 单例连接减少重复打开开销;onversionchange 主动关闭连接避免阻塞升级。
    • withStore 在 error/abort 时自动重试一次,提升 Service Worker 重启后的鲁棒性。
  • 查询优化
    • token-stats 使用 sessionId 索引与 KeyRange 缩小扫描范围;daily/range 聚合使用 timestamp 索引。
    • searchActiveSessionsMessages 在指定 sessionId 时使用 O(1) 主键查询,避免全量加载。
  • 双写与降级
    • ReAct checkpoint 先写 chrome.storage.local 再写 IndexedDB,确保即使 IndexedDB 不可用也不丢数据;读取时优先 IndexedDB,失败降级到 chrome.storage.local。
  • 一致性保证
    • withStore 仅在 tx.oncomplete 后 resolve,确保事务提交后再返回结果。
    • React loop 在 persistCheckpoint 后读回验证,发现空结果记录告警。
  • 存储空间管理
    • 归档会话最多保留 20 条,超出截断。
    • ReAct checkpoint 提供 TTL 清理(7 天),应在 Service Worker 启动时调用。
    • 迁移完成后清理 chrome.storage.local 的大数据 key,释放空间。

[本节为通用指导,无需具体文件分析]

故障排查指南 ​

  • IndexedDB 升级被阻塞
    • 现象:reactCheckpoints store 无法创建,checkpoint 写入静默失败。
    • 原因:其他连接持有旧版本未关闭。
    • 处理:db.js 已在 onversionchange 中主动关闭当前连接并重置缓存;确保调用方在升级后重试 getDB。
  • Store 不存在
    • 现象:withStore 报错 store 不存在。
    • 处理:withStore 检测到 store 不存在时会关闭连接并延迟重试一次;若仍失败则抛出明确错误。
  • Service Worker 重启后连接失效
    • 现象:transaction() 抛出同步异常。
    • 处理:withStore 捕获异常后 resetDBConnection 并重试一次。
  • Checkpoint 写入成功但读回为空
    • 现象:persistCheckpoint 返回 true,但 getReactCheckpoint 返回 null。
    • 处理:background/react-loop.js 在读回验证失败时记录错误日志,提示可能是 store 不存在或事务未提交。

章节来源

结论 ​

数据存储层通过 IndexedDB 封装层提供了可靠的连接管理、事务处理、版本升级与 store 缺失恢复能力;会话存储适配器实现了完整的会话生命周期管理与状态同步;ReAct checkpoint 的双写策略确保了高可用性与数据不丢失;Token 统计存储提供了高效的索引优化与多维聚合;配置管理系统支持灵活的导入策略、默认值合并与版本迁移。整体设计兼顾性能、一致性与可维护性,适合在 Chrome 扩展 MV3 环境下稳定运行。

[本节为总结,无需具体文件分析]

附录:数据模型与操作示例 ​

数据模型概览 ​

  • sessions:活跃会话,keyPath=id,索引 updatedAt。
  • activeSession:单条记录 key=active,存储当前活跃会话 ID。
  • archivedSessions:归档会话,keyPath=id,索引 createdAt。
  • uiPrototypes:UI 原型,keyPath=id,索引 createdAt/sessionId。
  • tokenStats:Token 统计,keyPath=id,索引 sessionId/timestamp。
  • reactCheckpoints:ReAct checkpoint,keyPath=sessionId,索引 updatedAt。
  • bookmarks:消息收藏,keyPath=id,索引 sessionId/createdAt。
  • scheduledTasks:定时任务,keyPath=id,索引 nextRunAt/enabled。
erDiagram
SESSIONS {
string id PK
string title
string model
boolean useTools
array enabledTools
string agentId
number temperature
number topP
array messageHistory
number scrollPosition
string createdAt
string updatedAt
number order
boolean isGenerating
array lastExecutionLog
}
ACTIVE_SESSION {
string key PK
string sessionId
}
ARCHIVED_SESSIONS {
string id PK
string title
string createdAt
array messages
}
TOKEN_STATS {
string id PK
string sessionId
string timestamp
string model
number promptTokens
number completionTokens
number totalTokens
number contextWindow
number contextUsageRate
string callType
}
REACT_CHECKPOINTS {
string sessionId PK
array currentMessages
array executionLog
number iteration
object globalIteration
object subtaskPlan
number currentSubtaskIndex
number totalReflectionRounds
string model
object apiParams
object taskContext
number tabId
string callId
string interruptedReason
number createdAt
number updatedAt
}
BOOKMARKS {
string id PK
string sessionId
string messageId
string content
string sessionTitle
string createdAt
boolean pinned
}
SCHEDULED_TASKS {
string id PK
string name
string cron
number nextRunAt
boolean enabled
}
SESSIONS ||--o{ BOOKMARKS : "messages referenced by"
TOKEN_STATS ||--|| SESSIONS : "by sessionId"
REACT_CHECKPOINTS ||--|| SESSIONS : "by sessionId"

图表来源

常见操作示例(路径引用) ​

[本节为示例路径引用,不包含具体代码内容]