# KV 存储设计 **本文档引用的文件** - [src/index.ts](file://src/index.ts) - [src/command/index.ts](file://src/command/index.ts) - [src/command/handlers/login.ts](file://src/command/handlers/login.ts) - [src/command/handlers/logout.ts](file://src/command/handlers/logout.ts) - [src/scheduler/index.ts](file://src/scheduler/index.ts) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts) - [wrangler.jsonc](file://wrangler.jsonc) - [.dev.vars](file://.dev.vars) - [worker-configuration.d.ts](file://worker-configuration.d.ts) - [package.json](file://package.json) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考虑](#性能考虑) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本文件面向 Cloudflare KV 存储在该项目中的设计与实现,重点围绕 KVAdapter 的配置与使用、COSMOE_STORAGE 命名空间的设置与环境变量配置、键值设计原则(会话数据、用户状态、临时数据)、数据序列化与反序列化策略(含错误处理与格式验证)、性能优化建议(数据大小限制、读写最佳实践)、存储容量规划、成本控制与监控指标进行系统性说明。内容基于仓库实际代码进行分析,确保可追溯与可落地。 ## 项目结构 该项目为一个基于 Cloudflare Workers 的 Telegram Bot,采用 KV 存储分别承载两类数据: - 用户凭证存储:COSMOE_CREDENTIALS(KVNamespace) - 会话与临时状态存储:COSMOE_STORAGE(KVNamespace) KV 命名空间在 Wrangler 配置中定义,并通过环境绑定到 Worker 运行时;Bot 主入口负责初始化命令与调度任务,命令模块通过 KVAdapter 将会话状态持久化至 COSMOE_STORAGE;调度模块使用 COSMOE_STORAGE 记录最新事件 ID,并从 COSMOE_CREDENTIALS 获取已注册用户列表以推送新活动通知。 ```mermaid graph TB subgraph "Cloudflare Workers" SRC_INDEX["src/index.ts"] CMD_INDEX["src/command/index.ts"] SCHED_INDEX["src/scheduler/index.ts"] LOGIN_H["src/command/handlers/login.ts"] LOGOUT_H["src/command/handlers/logout.ts"] CLIENT["src/client/cosmoe.ts"] end WRANGLER["wrangler.jsonc"] DEV_VARS[".dev.vars"] KV1["COSMOE_CREDENTIALS
KVNamespace"] KV2["COSMOE_STORAGE
KVNamespace"] WRANGLER --> KV1 WRANGLER --> KV2 DEV_VARS --> SRC_INDEX SRC_INDEX --> CMD_INDEX SRC_INDEX --> SCHED_INDEX CMD_INDEX --> KV2 CMD_INDEX --> KV1 LOGIN_H --> KV1 LOGOUT_H --> KV1 SCHED_INDEX --> KV1 SCHED_INDEX --> KV2 CMD_INDEX --> CLIENT ``` 图表来源 - [src/index.ts](file://src/index.ts#L13-L46) - [src/command/index.ts](file://src/command/index.ts#L20-L52) - [src/scheduler/index.ts](file://src/scheduler/index.ts#L12-L88) - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L13-L74) - [src/command/handlers/logout.ts](file://src/command/handlers/logout.ts#L10-L34) - [wrangler.jsonc](file://wrangler.jsonc#L21-L30) 章节来源 - [src/index.ts](file://src/index.ts#L6-L11) - [wrangler.jsonc](file://wrangler.jsonc#L21-L30) ## 核心组件 - KVAdapter:用于将 grammY 的会话存储抽象映射到 Cloudflare KV,实现 read/write/delete 操作。 - COSMOE_STORAGE:承载会话状态与临时数据(如“最新活动 ID”)。 - COSMOE_CREDENTIALS:承载用户凭证(Telegram 用户 ID -> Cosmoe 凭证)。 - 环境接口 Env:统一声明 BOT_TOKEN、BOT_INFO、COSMOE_CREDENTIALS、COSMOE_STORAGE。 - Wrangler 配置:定义 KV 命名空间绑定与运行参数。 章节来源 - [src/command/index.ts](file://src/command/index.ts#L20-L52) - [src/index.ts](file://src/index.ts#L6-L11) - [wrangler.jsonc](file://wrangler.jsonc#L21-L30) ## 架构总览 KV 存储在该应用中的职责划分如下: - 会话状态:通过 KVAdapter 将 grammY 会话状态持久化到 COSMOE_STORAGE,支持 JSON 序列化与反序列化。 - 用户凭证:登录成功后将 Cosmoe 用户 ID 与 token 写入 COSMOE_CREDENTIALS,按 Telegram 用户 ID 作为键。 - 临时数据:调度任务使用 COSMOE_STORAGE 记录“最新活动 ID”,避免重复推送。 - 用户列表:调度任务通过 COSMOE_CREDENTIALS.list() 获取所有已注册用户 ID,向其推送新活动。 ```mermaid sequenceDiagram participant User as "Telegram 用户" participant Bot as "Bot 实例" participant Cmd as "命令模块" participant KV as "COSMOE_STORAGE" participant Creds as "COSMOE_CREDENTIALS" participant API as "Cosmoe API 客户端" User->>Bot : "/login" Bot->>Cmd : 进入交互式登录对话 Cmd->>API : 获取 token API-->>Cmd : 返回 token Cmd->>Creds : put(Telegram 用户ID, JSON 凭证) Cmd-->>User : 登录成功 User->>Bot : 发送任意消息 Bot->>Cmd : 读取会话状态 Cmd->>KV : read(会话键) KV-->>Cmd : JSON 字符串 Cmd->>Cmd : JSON.parse() Cmd-->>User : 回复基于会话状态 ``` 图表来源 - [src/command/index.ts](file://src/command/index.ts#L20-L52) - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L49-L63) - [src/client/cosmoe.ts](file://src/client/cosmoe.ts#L124-L140) ## 详细组件分析 ### KVAdapter 配置与使用 - 在命令模块中创建 KVAdapter 并封装 read/write/delete,实现会话状态的持久化。 - 读取时对 KV 返回的字符串进行 JSON.parse,异常时返回 undefined 并记录日志。 - 写入时对对象进行 JSON.stringify,异常时记录错误但不中断流程。 - 该适配器直接依赖 Worker 运行时提供的 KVNamespace 接口。 ```mermaid flowchart TD Start(["进入会话读取"]) --> KVRead["KVAdapter.read(key)"] KVRead --> HasValue{"KV 是否有值?"} HasValue --> |否| ReturnUndef["返回 undefined"] HasValue --> |是| Parse["JSON.parse(value)"] Parse --> ParseOK{"解析是否成功?"} ParseOK --> |否| LogErr["记录错误并返回 undefined"] ParseOK --> |是| ReturnParsed["返回解析结果"] ReturnUndef --> End(["结束"]) LogErr --> End ReturnParsed --> End ``` 图表来源 - [src/command/index.ts](file://src/command/index.ts#L25-L49) 章节来源 - [src/command/index.ts](file://src/command/index.ts#L20-L52) ### COSMOE_STORAGE 命名空间设置与环境变量 - Wrangler 配置中定义 COSMOE_STORAGE 绑定到 KVNamespace,并提供唯一标识 ID。 - 运行时通过环境接口 Env 暴露 COSMOE_STORAGE,供命令模块与调度模块使用。 - .dev.vars 提供 BOT_TOKEN 等开发期变量,BOT_INFO 由 Wrangler vars 注入。 章节来源 - [wrangler.jsonc](file://wrangler.jsonc#L21-L30) - [src/index.ts](file://src/index.ts#L6-L11) - [.dev.vars](file://.dev.vars#L1-L2) ### 键值设计原则 - 用户凭证键:以 Telegram 用户 ID 为键,值为包含 user_id、token、timestamp 的对象 JSON 字符串。 - 会话数据键:grammY 会话键由框架生成,KVAdapter 负责读写;键名应具备唯一性且与会话上下文关联。 - 临时数据键:例如“latestEventId”,用于调度任务去重与增量推送。 章节来源 - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L50-L63) - [src/scheduler/index.ts](file://src/scheduler/index.ts#L27-L78) ### 数据序列化与反序列化策略 - 写入策略:将对象 JSON.stringify 后存入 KV,保证跨语言与跨平台兼容。 - 读取策略:从 KV 读取字符串后 JSON.parse,若解析失败则记录错误并返回 undefined,避免影响后续流程。 - 错误处理:捕获 KV 读写异常并记录日志,调用方根据返回值进行容错处理。 - 格式验证:建议在写入前对对象结构进行校验(如必填字段、类型),并在读取后进行二次校验(如时间戳有效性)。 章节来源 - [src/command/index.ts](file://src/command/index.ts#L25-L49) ### 使用场景与数据流向 - 登录流程:用户输入凭据 -> 调用 Cosmoe API -> 成功后以 Telegram 用户 ID 为键写入凭证。 - 会话流程:用户与 Bot 交互 -> grammY 通过 KVAdapter 读取/更新会话状态。 - 调度流程:定时任务读取最新活动 ID -> 获取新活动 -> 遍历已注册用户 -> 推送消息 -> 更新最新活动 ID。 章节来源 - [src/command/handlers/login.ts](file://src/command/handlers/login.ts#L49-L63) - [src/scheduler/index.ts](file://src/scheduler/index.ts#L24-L84) ## 依赖关系分析 - grammY 会话插件依赖 @grammyjs/storage-cloudflare 提供的 KVAdapter。 - 项目依赖 @grammyjs/conversations、@grammyjs/storage-cloudflare、grammy。 - KVNamespace 类型定义来自 worker-configuration.d.ts,包含 get/put/list/delete 等方法签名。 ```mermaid graph LR GRAMMY["@grammyjs/conversations"] --> ADAPTER["@grammyjs/storage-cloudflare"] ADAPTER --> KVNS["KVNamespace 接口"] APP["命令模块"] --> ADAPTER APP --> KVNS SCHED["调度模块"] --> KVNS ``` 图表来源 - [package.json](file://package.json#L18-L22) - [worker-configuration.d.ts](file://worker-configuration.d.ts#L1809-L1841) 章节来源 - [package.json](file://package.json#L18-L22) - [worker-configuration.d.ts](file://worker-configuration.d.ts#L1809-L1841) ## 性能考虑 - 数据大小限制 - KV 单条记录上限为 25 MiB(文本/JSON),建议将大型对象拆分或压缩后存储。 - 对于会话状态,尽量保持精简,避免频繁写入大对象。 - 读写最佳实践 - 批量读取:使用 KVNamespace.list(prefix) 获取键集合,再批量 get,减少网络往返。 - 缓存策略:对热点键设置合理的 cacheTtl,降低冷读延迟。 - 异步写入:将非关键路径的写入异步化,避免阻塞主流程。 - 命名空间分离 - COSMOE_CREDENTIALS 与 COSMOE_STORAGE 分离,避免相互影响与锁竞争。 - 调度任务优化 - 使用 list() 获取用户列表时注意分页游标(cursor),避免一次性拉取过多键。 - 对推送消息进行幂等处理,避免重复发送。 [本节为通用性能建议,无需特定文件引用] ## 故障排查指南 - KV 读取失败 - 现象:会话状态为空或解析报错。 - 处理:检查 KVAdapter 的 read 流程,确认 JSON.parse 是否抛错;查看日志定位具体键。 - KV 写入失败 - 现象:会话状态未持久化或出现异常。 - 处理:检查 KVAdapter 的 write 流程,确认 JSON.stringify 是否成功;查看日志定位异常。 - 用户凭证缺失 - 现象:执行登出或需要凭证的操作时报错。 - 处理:确认登录流程是否成功写入;检查 Telegram 用户 ID 是否正确;必要时清理无效键。 - 调度任务未推送 - 现象:新活动未通知用户。 - 处理:检查 COSMOE_STORAGE 中 latestEventId 是否更新;确认 COSMOE_CREDENTIALS.list() 是否返回用户键;逐个用户测试消息发送。 章节来源 - [src/command/index.ts](file://src/command/index.ts#L25-L49) - [src/command/handlers/logout.ts](file://src/command/handlers/logout.ts#L10-L34) - [src/scheduler/index.ts](file://src/scheduler/index.ts#L24-L84) ## 结论 本项目通过 KVAdapter 将 grammY 会话状态与 Cloudflare KV 无缝集成,结合 COSMOE_CREDENTIALS 与 COSMOE_STORAGE 的职责分离,实现了用户凭证、会话状态与临时数据的清晰分层。通过合理的键命名、JSON 序列化策略与错误处理机制,保障了功能可用性与可维护性。建议在生产环境中进一步完善数据结构校验、批量读写与缓存策略,以提升性能与稳定性。 [本节为总结性内容,无需特定文件引用] ## 附录 ### KV 命名空间与环境变量配置清单 - Wrangler 配置 - COSMOE_CREDENTIALS:绑定名称与 KV ID - COSMOE_STORAGE:绑定名称与 KV ID - vars:BOT_INFO(Bot 元信息) - 开发环境变量 - BOT_TOKEN:Bot 访问令牌 - 运行时环境接口 - Env:包含 BOT_TOKEN、BOT_INFO、COSMOE_CREDENTIALS、COSMOE_STORAGE 章节来源 - [wrangler.jsonc](file://wrangler.jsonc#L18-L30) - [.dev.vars](file://.dev.vars#L1-L2) - [src/index.ts](file://src/index.ts#L6-L11) ### 关键流程时序图(调度任务) ```mermaid sequenceDiagram participant Cron as "计划任务触发" participant Sched as "调度模块" participant KV as "COSMOE_STORAGE" participant Creds as "COSMOE_CREDENTIALS" participant API as "Cosmoe API" participant TG as "Telegram API" Cron->>Sched : 触发 scheduled Sched->>KV : get("latestEventId") Sched->>API : getEvents() API-->>Sched : 返回事件列表 Sched->>Creds : list() Creds-->>Sched : 返回用户键集合 loop 遍历新事件 Sched->>TG : sendMessage(用户ID, 活动消息) TG-->>Sched : 发送结果 end Sched->>KV : put("latestEventId", 最大新ID) ``` 图表来源 - [src/scheduler/index.ts](file://src/scheduler/index.ts#L12-L88)