Skip to content

feat: 边缘查询 scan + 谓词下推——只回传命中切片(方向 B,热窗口)#139

Merged
NeverENG merged 5 commits into
mainfrom
feat/edge-scan
Jun 14, 2026
Merged

feat: 边缘查询 scan + 谓词下推——只回传命中切片(方向 B,热窗口)#139
NeverENG merged 5 commits into
mainfrom
feat/edge-scan

Conversation

@NeverENG

Copy link
Copy Markdown
Owner

背景(采集员痛点 #3:查不动数据)

此前只有 PUT/GET/DELETE,想「把刚才 10s 内 az>9.9 的帧捞出来只传这一段」做不到。本 PR 把边缘本地查询 + 只回传命中切片端到端跑通。

范围(已与用户确认:热窗口)

v1 扫 MemTable 未刷盘热数据——正好是采集员最常查的「最近窗口」。已刷盘到 SSTable 的历史数据为 Phase 2(需把 mergeIterator 泛化+跨层新值优先+并发快照,已在代码注释标注边界)。

改动(4 个原子 commit)

  1. storage MemTable.ScanRange:闭区间升序、firstGTE 定位、2 路归并 active(最新) 覆盖 dirty、跳墓碑、fn 可提前停;全程持读锁(热窗口扫描有界)
  2. pkg/predicate:最小字段谓词 field op value> >= < <= == !=,数值/字符串自动判别),纯函数
  3. pkg/proto + serviceSCAN msgID + wire 编解码(server/client 共用)+ KVServer.Scan(谓词下推、命中拷贝、10000 封顶并告警)+ 路由注册 + metrics.Scans
  4. clientSendScan + 交互式 scan <start|-> <end|-> [field op operand]

差异化

谓词在服务端下推,只把命中切片回传——这是「GB→MB」的核心,也是 MCAP/Zenoh/COS 都不在采集入口做的那件事。

测试

  • 单测:ScanRange(范围/墓碑/active 覆盖 dirty/提前停)、predicate(13 例)、proto 编解码往返
  • 端到端集成测试:写 5 帧 IMU → scan [imu:dev0:100,imu:dev0:299] az>9.9 → 只回 2 条命中、越界设备 dev1 被范围排除
  • go build ./... 通过,既有测试无回归

用法:

> scan imu:dev0:100 imu:dev0:200 az > 9.9
命中 2 条:
  imu:dev0:150 = {"az":9.95}
  imu:dev0:200 = {"az":10.2}

🤖 Generated with Claude Code

NeverENG and others added 4 commits June 14, 2026 21:18
为边缘查询提供存储层基础:
- SkipList.firstGTE 定位起点,level-0 有序遍历
- ScanRange 全程持读锁,2 路归并 active(最新)+dirty,新值优先、墓碑遮蔽
- start/end 空表示该侧不限;fn 返回 false 提前停止
- Engine.Scan 转发;当前仅覆盖未刷盘热数据,SSTable 历史待 Phase 2

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
支持 > >= < <= == !=,数值/字符串自动判别;无谓词全匹配。
覆盖具身采集常见查询(如 az>9.9 / status==ok),不引入完整表达式语言。
纯函数、零业务依赖,供边缘查询服务端筛选、只回传命中切片。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- pkg/proto: SCAN msgID 与请求/响应 wire 编解码(server/client 共用)
- KVServer.Scan: 扫 MemTable 热数据,谓词下推筛选,命中拷贝返回,10000 条封顶并告警
- router.handleScan + 两个 Server 注册 SCAN 路由
- metrics 增 Scans 计数并入快照
谓词由钩子放行(仅 PUT 过滤),SCAN 原样直达。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- client.SendScan 编解码 SCAN 请求/响应
- 交互式 scan <start|-> <end|-> [field op operand],- 表示该侧不限
- service 集成测试:写 IMU 帧→范围+谓词扫描→只回命中、越界设备排除

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@NeverENG, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 30 minutes and 17 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fcf7118d-63c3-481b-a58e-d55eab122a55

📥 Commits

Reviewing files that changed from the base of the PR and between bb79547 and 83ad15b.

📒 Files selected for processing (21)
  • Server/server.go
  • Server/server_pprof.go
  • client/client.go
  • client/interactive.go
  • config/config.json
  • config/global.go
  • config/global_test.go
  • network/banNet/wire_scan_test.go
  • pkg/metrics/metrics.go
  • pkg/predicate/predicate.go
  • pkg/predicate/predicate_test.go
  • pkg/proto/codes.go
  • pkg/proto/scan.go
  • pkg/proto/scan_test.go
  • service/fsm.go
  • service/router.go
  • service/scan_integration_test.go
  • storage/engine.go
  • storage/istorage/IMemTable.go
  • storage/zstorage/memtable.go
  • storage/zstorage/scan_test.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/edge-scan

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 14, 2026

Copy link
Copy Markdown

🐯 BanGD 数据库内核评审

整体风险:🟡 中

变更总结:本 PR 在 BanDB 存储引擎的热路径——MemTable 层新增了范围扫描(ScanRange)功能,并配合前端的谓词下推(predicate)、wire 编解码、交互式客户端与网络包上限调整,端到端实现了「边缘本地范围查询 + 服务端筛选后只回传命中切片」的能力。

核心改动集中在四层:

  1. 存储层MemTable.ScanRange——在 MemTable 的 two-list 结构(active + dirty)上做双向归并扫描,firstGTE 跳表定位,闭区间升序迭代,跳过墓碑(value==nil),fn 可提前停止;全程持读锁。
  2. 谓词层pkg/predicate):纯函数式的单字段谓词引擎,支持 > >= < <= == !=,按操作数类型自动分流(数值比较 vs 字符串相等)。
  3. 协议与网络proto.ScanRequest/proto.ScanEntry 编解码,KVServer.Scan(谓词下推 + 命中拷贝 + 10000 条上限),MsgScan 路由注册;MaxPackageSize 从 1KiB 提升至 16MiB。
  4. 客户端Client.SendScan + 交互式 scan 命令。

注释已标注 Phase 2 边界:历史 SSTable 数据暂未接入,当前仅扫 MemTable 热窗口。

本评审不阻塞合入;架构级建议以 Issue 形式跟踪,普通问题在下方内联列出。

架构问题(共 4 项)

普通问题(共 3 项)

🛑 [阻塞 · 逻辑错误] pkg/proto/scan.go:83 DecodeScanResponse 空条目时解包逻辑可导致越界

  • count == 0 时,DecodeScanResponseoff = 1+statusLen+4 位置返回空切片,且循环体不执行,这是正确的。但当 entriesEncodeScanResponse 编码时传入了 nil(而非 []ScanEntry{}),编码侧 len(nil)=0,编码正常;解码侧也能正常处理。不过若 count > 0 但 payload 在条目数据中间被截断,off+8 > len(payload) 检测在循环内会触发。目前代码已有 off+8 > len(payload) 检查,是安全的。但 keyLen < 0 || valueLen < 0 的检查在 int(uint32(...)) 下不可能为负——uint32 总是非负,int 转换后也不会负。这个检查实际上是多余的(但无害)。
  • 建议:移除 keyLen < 0 || valueLen < 0 死检查,或改为 keyLen == 0 && valueLen == 0 时的异常处理(如果业务上不允许空 key)。

💡 [建议 · 逻辑错误] pkg/proto/scan.go:68 DecodeScanRequest 的 operand 切片包含不可预见的尾部数据

  • DecodeScanRequestoperand := data[off:] 取剩余全部字节。这在理论上是正确的——operand 就是请求尾部剩余。但若请求格式有额外扩展字段或填充被追加到尾部未在 scanReqHeaderLen 中描述,operand 会错误吞掉这些字节。当前协议下这是设计如此(operand 是变长剩余部分),但应备注此项约定。
  • 建议:在包注释或 const scanReqHeaderLen 旁注明「operand 占 request payload 剩余全部字节,无显式长度字段」。

💡 [建议 · 错误处理] client/interactive.go:181 parseOp 不处理操作数中有空格的分词问题

  • handleScanstrings.Fields 分词后,operand 从 parts[5:]strings.Join 拼接。但如果操作数本身包含空格(如 az != "some long value"),Fields 会把值拆成多个 token,parts[4] 不会是完整的 operator 而可能被误认为是 operand 的一部分。这是 CLI 分词的固有限制,当前不影响(数字操作数无空格),但未来扩展时会有歧义。
  • 建议:考虑在 PR 描述或注释中注明当前 scan CLI 的操作数不支持内部空格;或改为更鲁棒的解析方式。

本次评审消耗 token:共 323462 tokens(输入 303072,输出 5926,缓存命中 14464,缓存写入 0)|维度 [concurrency, memory, lock, storage, performance]|对抗式复核 3 票/条,过滤疑似误报 0 条

1024B 上限会让客户端 UnPack 拒收多条 SCAN 响应,也使 >1KB 的相机帧无法 PUT
(UnPack 对收发双方都生效)。提到 16MiB 容纳多模态大值与范围查询结果。
新增 banNet 网络缝回归测试:200 条命中的响应需完整通过 Pack→UnPack→解码。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@NeverENG NeverENG merged commit c051e47 into main Jun 14, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant