Skip to content

declared ≠ enforced:引擎 multi:true 谓词写入没有 data.record.* 事件,webhook / 知识同步对其静默(REST 批量端点不受影响) #4639

Description

@os-zhuang

#4626(subscribeData/DataEvent 生产者履约)的范围外发现,按第十条军规立案,unassigned。

现象

DataEvent(@objectstack/spec/api)是逐记录契约:recordId 必填。但 ObjectQL 引擎的批量写路径拿不到记录标识:

  • IDataDriver.updateMany / deleteMany 的契约返回值是 Promise<number>(受影响行数,见 packages/spec/src/contracts/data-driver.ts),没有行、也没有 id;
  • 引擎 update() / delete()options.multi = true 分支就走这两个方法。

#4626 之前,这两条路径各发一条伪造事件:payload: { recordId: '', changes, after: <受影响条数> }。它同时骗了两个仓内消费者——webhook auto-enqueuer 收到 recordId: '' 的一次投递,知识同步则把 {recordId, after} 这个信封当成记录写进索引。#4626 按契约优先(不在消费端容错、也不在生产端伪造)改成:批量写不发事件,并 warn 出声

于是留下这个真实的能力缺口:

  • multi: true 的更新/删除不再触发 webhook(此前触发一次,但内容不可用);
  • 知识库 object source 对批量写不同步;
  • subscribeData 订阅者看不到批量写。

处置方向(需裁决)

  1. 逐记录扇出:批量写前/后按 AST 取一次匹配行(或让驱动 updateMany/deleteMany 返回受影响 id),对每行发一条合规 DataEvent。语义最正,但热路径多一次查询(或改驱动契约,涉及全部驱动)。
  2. 新增批量事件类型:在 DataEventType 里声明 data.records.updated / data.records.deleted(带 object + matched 计数 + 可选的 where),让批量写有自己的诚实契约,而不是冒充逐记录事件。消费者(auto-enqueuer / 知识同步 / client SDK)按新类型分别处理。
  3. 维持现状:批量写没有实时事件,文档写清楚(client realtime declared ≠ enforced:subscribeData 回调把 RealtimeEventPayload 硬铸成 DataEvent,顶层 recordId/changes/id 运行时是 undefined #4626 已在 content/docs/automation/webhooks.mdx 写明)。

三条都要顺带回答:批量事件是否应进入 webhook 触发器词汇表(create/update/delete 目前是逐记录语义)。

参考

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions