KV 存储设计.md 13 KB

KV 存储设计

本文档引用的文件

  • src/index.ts
  • src/command/index.ts
  • src/command/handlers/login.ts
  • src/command/handlers/logout.ts
  • src/scheduler/index.ts
  • src/client/cosmoe.ts
  • wrangler.jsonc
  • .dev.vars
  • worker-configuration.d.ts
  • 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 获取已注册用户列表以推送新活动通知。

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<br/>KVNamespace"]
KV2["COSMOE_STORAGE<br/>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
  • src/command/index.ts
  • src/scheduler/index.ts
  • src/command/handlers/login.ts
  • src/command/handlers/logout.ts
  • wrangler.jsonc

章节来源

  • src/index.ts
  • wrangler.jsonc

核心组件

  • 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
  • src/index.ts
  • wrangler.jsonc

架构总览

KV 存储在该应用中的职责划分如下:

  • 会话状态:通过 KVAdapter 将 grammY 会话状态持久化到 COSMOE_STORAGE,支持 JSON 序列化与反序列化。
  • 用户凭证:登录成功后将 Cosmoe 用户 ID 与 token 写入 COSMOE_CREDENTIALS,按 Telegram 用户 ID 作为键。
  • 临时数据:调度任务使用 COSMOE_STORAGE 记录“最新活动 ID”,避免重复推送。
  • 用户列表:调度任务通过 COSMOE_CREDENTIALS.list() 获取所有已注册用户 ID,向其推送新活动。

    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
  • src/command/handlers/login.ts
  • src/client/cosmoe.ts

详细组件分析

KVAdapter 配置与使用

  • 在命令模块中创建 KVAdapter 并封装 read/write/delete,实现会话状态的持久化。
  • 读取时对 KV 返回的字符串进行 JSON.parse,异常时返回 undefined 并记录日志。
  • 写入时对对象进行 JSON.stringify,异常时记录错误但不中断流程。
  • 该适配器直接依赖 Worker 运行时提供的 KVNamespace 接口。

    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

章节来源

  • src/command/index.ts

COSMOE_STORAGE 命名空间设置与环境变量

  • Wrangler 配置中定义 COSMOE_STORAGE 绑定到 KVNamespace,并提供唯一标识 ID。
  • 运行时通过环境接口 Env 暴露 COSMOE_STORAGE,供命令模块与调度模块使用。
  • .dev.vars 提供 BOT_TOKEN 等开发期变量,BOT_INFO 由 Wrangler vars 注入。

章节来源

  • wrangler.jsonc
  • src/index.ts
  • .dev.vars

键值设计原则

  • 用户凭证键:以 Telegram 用户 ID 为键,值为包含 user_id、token、timestamp 的对象 JSON 字符串。
  • 会话数据键:grammY 会话键由框架生成,KVAdapter 负责读写;键名应具备唯一性且与会话上下文关联。
  • 临时数据键:例如“latestEventId”,用于调度任务去重与增量推送。

章节来源

  • src/command/handlers/login.ts
  • src/scheduler/index.ts

数据序列化与反序列化策略

  • 写入策略:将对象 JSON.stringify 后存入 KV,保证跨语言与跨平台兼容。
  • 读取策略:从 KV 读取字符串后 JSON.parse,若解析失败则记录错误并返回 undefined,避免影响后续流程。
  • 错误处理:捕获 KV 读写异常并记录日志,调用方根据返回值进行容错处理。
  • 格式验证:建议在写入前对对象结构进行校验(如必填字段、类型),并在读取后进行二次校验(如时间戳有效性)。

章节来源

  • src/command/index.ts

使用场景与数据流向

  • 登录流程:用户输入凭据 -> 调用 Cosmoe API -> 成功后以 Telegram 用户 ID 为键写入凭证。
  • 会话流程:用户与 Bot 交互 -> grammY 通过 KVAdapter 读取/更新会话状态。
  • 调度流程:定时任务读取最新活动 ID -> 获取新活动 -> 遍历已注册用户 -> 推送消息 -> 更新最新活动 ID。

章节来源

  • src/command/handlers/login.ts
  • src/scheduler/index.ts

依赖关系分析

  • grammY 会话插件依赖 @grammyjs/storage-cloudflare 提供的 KVAdapter。
  • 项目依赖 @grammyjs/conversations、@grammyjs/storage-cloudflare、grammy。
  • KVNamespace 类型定义来自 worker-configuration.d.ts,包含 get/put/list/delete 等方法签名。

    graph LR
    GRAMMY["@grammyjs/conversations"] --> ADAPTER["@grammyjs/storage-cloudflare"]
    ADAPTER --> KVNS["KVNamespace 接口"]
    APP["命令模块"] --> ADAPTER
    APP --> KVNS
    SCHED["调度模块"] --> KVNS
    

图表来源

  • package.json
  • worker-configuration.d.ts

章节来源

  • package.json
  • worker-configuration.d.ts

性能考虑

  • 数据大小限制
    • 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
  • src/command/handlers/logout.ts
  • src/scheduler/index.ts

结论

本项目通过 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
  • .dev.vars
  • src/index.ts

关键流程时序图(调度任务)

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