Skip to content

[BUG] Cloudflare KV binding 模式下 kvDriver.list() 沿用 EdgeOne 语义,字段名不匹配导致 key 格式下初始化 500 并耗尽 list 配额 #125

Closed

Description

@zhuzhu88920

请确认以下事项

  • 我确认描述清晰有礼貌,已提供必要诊断信息,在最新版本上可以复现。
  • 我确认已经查过相关文档和已有问题和讨论,本次反馈并非是重复提问。
  • 我确认这是Worker项目问题,并非网络和操作导致,也不是Go版的问题。
  • 我确认已阅读开源许可/免责条款/使用政策,理解使用的风险自行承担。

版本(必填)

main @ d3b79f8 (2026-10-11)

平台(必填)

Cloudflare Workers

DB_FORMAT(必填)

key(分 key)

DB_DRIVER(必填)

kv

存储驱动(必填)

无(问题发生在创建管理员的初始化阶段,尚未挂载任何存储)

问题描述(必填)

现象

在 Cloudflare Workers 上使用 KV binding + DB_FORMAT=key 时,初始化创建管理员必定失败:
/api/public/init/setup 返回 500 Internal Server Error(Hono 未捕获异常)。
仅把 DB_FORMAT 改为 map 后,同一请求返回 200 {"code":200,"message":"success"},初始化成功。

根因

src/backend/internal/model/store/driver/kv.ts 第 451-482 行,binding 模式的 list() 是按 EdgeOne KV 语义实现的(代码注释亦明确写了这点):

const page = await kv.list({ prefix, cursor, limit: 256 })
const pageKeys = Array.isArray(page?.keys) ? page.keys : []
for (const item of pageKeys) {
  if (item?.key) keys.push(item.key)              // ← EdgeOne: item.key
}
if (pageKeys.length > 0) {
  cursor = pageKeys[pageKeys.length - 1].key || ""  // ← 同样是 item.key
}
complete = Boolean(page?.complete) || pageKeys.length === 0  // ← EdgeOne: page.complete

而 Cloudflare Workers KV 的 KVNamespaceListResult 实际返回:

{ keys: { name: string; expiration?: number; metadata?: unknown }[];
  list_complete: boolean; cursor?: string; cacheStatus: string | null }

两处字段名均不匹配:

  1. item.key → CF 为 item.name,因此 keys 恒为空数组,list 事实上失效;
  2. page.complete → CF 为 page.list_complete,恒为 undefined。

后果(两个叠加)

  • 存储为空时:pageKeys.length === 0 使 complete 为 true,循环侥幸退出(但返回空数组,功能已失效)。
  • 存储非空时:complete 恒为 false,且 cursor 被赋为 undefined || "" 即 "",于是每一轮都用相同游标重复拉取同一页,while 一路跑到 guard < 1000 上限 —— 单次 list() 调用就产生 1000 次真实 KV list 请求。

Cloudflare KV 免费版 list 配额为 1000 次/天(read 10 万/天、write 1000/天、delete 1000/天,每日 00:00 UTC 重置)。而 key 格式的 load() / save() 会对 6 张表逐表调用 list(),一次页面访问即可打满当天额度。额度耗尽后 list() 抛错 → saveDb() 抛异常 → Hono 返回裸 500。

附带问题

kvDriver.isAvailable() 只判断 binding 对象是否存在、不做真实读写探测,因此 /api/public/env_check 在存储实际不可写时仍返回「存储就绪」,严重误导排查(本次排查即因此多花大量时间)。

期望

  1. binding 模式改回 Cloudflare 语义(可参考同目录 cfkv.ts 的实现);
  2. 更关键的是配额设计层面:即使字段改对,key 格式「每次 load/save 对 6 张表调 list」在免费版 1000 次/天下仍不可持续(约 167 次访问/天即耗尽)。建议要么在文档中标注 key + kv 不适用于免费版,要么给 key 格式加一个索引键(用一次 get 换取 N 次 list)。

报错信息(必填)

  1. 初始化接口:
POST /api/public/init/setup
→ HTTP 500
→ body: Internal Server Error

(无 code/message JSON,属 Hono 未捕获异常的裸 500)

  1. 环境检查接口(同一时刻,全部"就绪",与存储实际不可写矛盾):
GET /api/public/env_check
→ storage.available=true, error_code=null, db_load_error=null,
  jwt.ready=true, resolved_driver=kv
  1. Cloudflare KV 命名空间控制台提示:
键-值条目无法列出。您在过去 24 小时内超出了计划所含的列出或读取配额。
  1. 切换为 map 后:
POST /api/public/init/setup
→ HTTP 200 {"code":200,"message":"success"}

GET /api/public/init_status
→ initialized:true, ready:true, db_trusted:true, db_load_error:null

说明:未能取到服务端堆栈 —— Workers Observability 需重新部署才会嵌入新版本生效,期间反复触发也拿不到日志。上述结论由源码逐行比对 + 控制台配额提示 + 变量对照实验三者交叉验证得出。

相关配置(必填)

// Cloudflare Dashboard → openlist-worker → 设置 → 运行时变量和密钥(生产)
ENVIRONMENT = production
DB_CIPHER   = none
DB_FORMAT   = key      // ← 出问题的值;改为 map 后正常
DB_DRIVER   = kv
JWT_SECRET  = (密钥/Secret 类型)

// 绑定
KV  → openlist-worker-kv

// wrangler.jsonc(仓库侧,与 Dashboard 一致)
"vars": { "ENVIRONMENT": "production", "DB_CIPHER": "none", "DB_FORMAT": "key", "DB_DRIVER": "kv" }

部署方式:GitHub 连接自动部署(fork 自 OpenListTeam/OpenList-Worker)
账户类型:Cloudflare Workers 免费版

复现链接(可选)

复现站点:https://list.zhuyongfu.cn (当前已切为 map 正常运行,故暂不可在线复现)

最小复现步骤:

  1. Cloudflare Workers 新建 Worker,绑定一个 KV 命名空间,binding 名 KV
  2. 设置变量 DB_DRIVER=kv、DB_FORMAT=key、JWT_SECRET=<任意32字节随机串>
  3. 部署后访问 https://<域名>/@init
  4. 环境检查显示「存储 就绪 / JWT 密钥 就绪」,点「继续」,填写管理员用户名与密码后提交
  5. POST /api/public/init/setup 返回 500

最小代码级复现(在任意 CF Worker 中执行):

const page = await env.KV.list({ prefix: "user:", limit: 256 })
console.log(page.list_complete, page.keys[0]?.name)  // true, "user:1"
console.log(page.complete, page.keys[0]?.key)        // undefined, undefined  ← kv.ts 正在读的就是这两个

参考:

Activity

  1. PIKACHUIM commented on Oct 16, 2026

    @PIKACHUIM
    Member

    已修复问题,但是KV只有1000次着实有点少了

    后面会加文档建议用D1或者map+kv

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions