kevy
本页内容

从 6.3 升级到 6.4

一句话版本:代码不用改,磁盘上的东西不会动。 没有删掉任何 API,数据目录双向都能打开,6.3.0 与 6.4.0 的节点无论谁做主库都能配对。改个号就完事了。

kevy-embedded = "6.4.0"

6.4.0 是一次质量发布,大部分内容从外面看不见:原本检查得比自称的少的测量装置,现在真的检查它说要检查的东西。但修真缺陷就会改真答案,其中有七处能被观察到。下面每一处都给出改前改后你会看到什么——是拿已发布的 6.3.0 与 6.4.0 实测对比出来的,不是从 diff 推出来的。

一句话对照表

如果你……会变什么§
跑服务器或某个语言绑定换二进制 / 升包号,别的什么都不用—
在纬度约 66° 以上用 GEOSEARCH / GEORADIUS原来漏掉的、在半径内的成员现在会返回1
设了 maxmemory 且用 APPEND 增长字符串used_memory 按实际分配计,驱逐会更贴近设定的上限2
把用户输入的 @now±N mo 或 @now±N y 当查询边界放不下的偏移现在会被拒绝,跟天数一直以来的行为一样3
在 SQL 折叠面里用 regexp_*后面还跟着模式的捕获型交替现在能匹配了4
在 Linux 上依赖 CLIENT KILL 或缓冲区上限客户端的连接现在真的会关掉5
抓取 INFOINFO clients 多了 blocked_clients;新增 INFO allocator6
从 Rust 直接嵌入 kevy-seg、kevy-ring 或 kevy-time三种原本会损坏数据或 panic 的输入,现在被拒绝或饱和处理7
拉 ghcr.io/goliajp/kevy:latest重新拉一次;这个 tag 现在指向 6.4.0—
从 npm 用 @goliapkg/kevy-node,或以嵌入方式用 @goliapkg/kevy-ts6.0.0–6.3.0 装得上但加载不了引擎;6.4.0 可以npm

1. GEOSEARCH 返回半径内的每一个成员

搜索只按半径决定格子大小。一个格子的经度宽度(以度计)是固定的,但搜索框的经度半宽按 1 / cos(纬度) 增长,所以越靠近两极,九个格子就越盖不住搜索框,半径内的成员会被悄无声息地跳过。

在纬度 84° 处、1 km 半径的 98% 位置放 360 个成员——每一个都在半径内:

返回数
6.3.094
6.4.0360

66° 以内一个都不丢;到 70° 丢了 360 个里的 63 个,越往极点越多。如果你的应用曾经为此绕行——放大半径、或者重复查询——现在不需要了。GEOSEARCH … BYRADIUS 0 也不再扫描整个键的所有成员:在 200,000 个成员的键上,从 9.23 ms 降到 0.026 ms,答案不变。

2. maxmemory 按字符串实际占用计费

用 APPEND 增长的字符串带着缓冲区的空余容量,而这部分空余原来没计费。2,000 个键,每个由七次 45 字节的 APPEND 构成:

used_memory 增长
6.3.0822,000
6.4.0912,000

差值正好是没用上的容量:每个键 45 字节。追加八次时两边一致,因为缓冲区恰好被填满。所以在设了 maxmemory 的情况下,原地增长值的工作负载读到的 used_memory 会高一些,驱逐也会更贴近设定的上限——这本来就是上限该有的含义。用 SET 整体写入的值不受影响。

3. 放不下的月、年偏移会被拒绝

IDX.QUERY 与 IDX.COUNT 接受 @now-7d 这样的边界。天、小时、分钟、秒和周遇到大到无法表示的偏移会拒绝;月和年却会给出答案:

边界6.3.06.4.0
@now-300000000000y:0报错
@now-768614336404564650mo:0报错
@now-1000y:1:1

一个不可能的边界静默返回 :0,就是一个错误的结果集。如果你把这类边界从用户输入直接传进来,报错才是正确答案;正常范围内的偏移行为跟以前完全一样。

4. regexp_*:捕获型交替会逐个分支重试

SELECT regexp_matches('abc', '(a|ab)c');        -- 6.3.0:无结果   6.4.0:{ab}
SELECT regexp_replace('abcd', '(a|ab)c', 'X');  -- 6.3.0:abcd     6.4.0:Xd

(a|ab)c 选了分支 a,让 c 去对 b,失败后再也没回头试 ab。写成 (ab|a)c 碰巧能用,所以一直没被发现:答案取决于分支的书写顺序。只有捕获括号受影响——(?:a|ab)c 一直是对的。受影响的入口是 kevy-cli sql eval 以及经 kevy-sql 编译的一切;服务器自己的命令不使用这个引擎。

5. Linux:服务器决定断开的连接,客户端现在真的收得到

在 io_uring 反应器上,服务器决定关闭的连接——查询缓冲区或输出缓冲区超限、或 CLIENT KILL——在客户端那边可能一直不关。服务器记了日志、也计了数;套接字就是没发出 FIN。在确认走 io_uring 路径的 runner 上,等着看到关闭的客户端,改前 100 次里漏掉 5 到 7 次,改后 100 次里 0 次。

如果你的客户端专门设了超时来应付"踢了但没生效",现在它会立刻看到连接断开。这个缺陷需要内核里还挂着一个 multishot 接收,而只有 io_uring 反应器会用到它;epoll 与 kqueue(macOS)反应器是直接 close(fd),不受影响。

6. INFO 的两处新增

  • INFO clients 报告 blocked_clients,与 Redis 一致:当前停在阻塞命令里的连接数,跨分片求和。
  • INFO allocator 是新增的,在启用 kevy-alloc feature 的构建里出现。它把每一个映射的字节分进具名的项——alloc_live、alloc_returned(已还给操作系统)、alloc_virgin(切出来但从没碰过)、alloc_hysteresis(清空后留着复用)、alloc_segment_overhead 等——这样常驻内存的比例就能被解释,而不只是被测出来。其他构建里没有这个段。

两者都是新增。没有任何已有字段改名或改含义。

7. 写给 Rust 嵌入用户

这些都不能从服务器命令触达;只在你直接用这些 crate 时才有关系。

  • kevy-seg:SegBuilder::push 遇到大到一页放不下的键会返回错误。原来 4,074 字节的键会写出一个能正常写入、正常打开、却读不回来的段,再大一点就 panic。manifest 记录长度字段里翻一个比特,也不再会悄悄把账本清空;页头声称的槽数多到放不下时,按损坏拒绝而不是 panic。
  • kevy-ring:ring(capacity) 在容量超过 usize 能表示的最大 2 的幂时,饱和到那个值。原来会返回一个第一次 push 就出错的环。
  • kevy-time:add_months 饱和而不再 panic;新增 checked_add_months 与 checked_epoch_from_civil。
  • kevy-bytes:SmallBytes::heap_bytes 现在报告分配的容量而不是长度——§2 背后就是这一处。

以上按 Cargo 的规则都不是破坏性改动。cargo-semver-checks 把 v6.4.0 的全部 40 个已发布库 crate 与 v6.3.0 对比,没有发现需要升 major 的地方(kevy-mcp 只有二进制、没有 API 可比)。


原样保留的部分

在一台 Linux 机器上对已发布的 6.3.0 二进制实测,不是假设的:

  • 数据目录。 6.3.0 写入、6.4.0 打开、再由 6.3.0 打开,每一个值都完好,包括大到进 value log 的值。没有迁移,没有单向门。
  • 复制。 6.3.0 主库配 6.4.0 副本能收敛,6.4.0 主库配 6.3.0 副本也能,所以可以按任意顺序一个节点一个节点地升。
  • 线协议,除了上面的 §1、§3 与 §6。

6.4.0 还改了什么——门禁能看到的部分

这次发布的大部分内容关乎代码仓库而不是引擎,完整内容在 changelog。简单说:每一个 unsafe 块现在都写明了它依赖的前提(原来 378 个没写);每一个公开类型都实现了 Debug;从没被任何门禁跑过的 loom 交错搜索套件,现在每次推送都会跑;编解码器的文档不再声称能检出它其实检不出的损坏,并写明完整性真正在哪一层保证;还有几处"检查通过、但没有真的检查它名下那件事"的测试装置被重做,改坏它们守护的代码就会让它们失败。这些都不会改变你收到的任何答案。


怎么拿到

# Cargo.toml
kevy-embedded = "6.4.0"
npm install @goliapkg/kevy@6.4.0          # wasm
npm install @goliapkg/kevy-node@6.4.0     # Node 原生
pip install kevy==6.4.0
go get github.com/goliajp/kevy-go/v6@v6.4.0
cargo install kevy --version 6.4.0        # 服务器二进制,从源码构建
npm install -g @goliapkg/kevy-bin@6.4.0   # 服务器二进制,预编译

@goliapkg/kevy-bin 从 6.4.0 起上了 npm:不装 Rust 工具链也能拿到 kevy 与 kevy-cli, 平台跟 release 二进制一样是那三个,字节也完全相同。

npm:kevy-node 与 kevy-ts 6.0–6.3 加载不了

@goliapkg/kevy-node 的原生引擎来自一个平台包(@goliapkg/kevy-node-linux-x64 等), 版本钉在它自己的版本上。6.0.0 到 6.3.0 这些平台包从来没发布过——npm 上只有 5.1.0—— 所以 kevy-node 能装上,npm 跳过了找不到的可选依赖,第一次 open() 就报 dlopen 错误, 错误里还是一个 target/debug 路径。@goliapkg/kevy-ts 钉的是同一组平台包,嵌入后端同样失败; 它的远程客户端(kevy://host:port)不受影响。

6.4.0 是第一个平台包在 npm 上的版本,2026-09-16 发布,并在 macOS arm64 与 Linux x86-64 上从 registry 实际安装验证过。之前的版本事后修不了:请升级到 6.4.0。

Linux(x86-64、arm64)与 macOS(arm64)的预编译服务器二进制,各带一个 SHA-256 文件,在 v6.4.0 release 上。用 ghcr.io/goliajp/kevy:latest 的容器用户,下次拉取就是 6.4.0;钉 tag 的话是 ghcr.io/goliajp/kevy:6.4.0。