背景
#3152 在 PR #3161 中按"缓解而非根治"落地:
- ✅ autonumber 移到校验之后——注定失败的批尝试不再消耗号段;
- ✅
engine.insert JSDoc 明确 beforeInsert 为 at-least-once 语义(钩子需幂等);
- ❌ 未根治:批中一行校验失败 → 整个
engine.insert(array) 抛错 → bulkWrite 降级逐行重跑完整 insert → 好行的 beforeInsert 钩子仍执行 2 次(副作用型钩子:发通知、计数器、外部 API 调用等重复执行)。
当时的结论(见 #3161 PR 描述):根治需要引擎级部分成功 API,超出 #3152 范围。本 issue 跟踪该根治。
方向
给批插入路径一个"部分成功"形态,让 bulkWrite 不再需要"整批失败 → 重跑整个 insert"这种放大钩子副作用的降级:
- 候选 A:
engine.insert(array, { partial: true }) 返回 per-row 结果({ok, record | error}[]),校验失败的行不再让整批抛错;好行只走一遍 hooks → 校验 → 驱动写入。
- 候选 B:新增独立入口(如
engine.insertMany),旧的 insert(array) 语义保持不变(仍整批原子抛错),调用方(seed/import 的 writeBatch 闭包)切到新入口。
- 无论哪种,坏行需在 beforeInsert 之后、驱动写入之前被剔除并报告,好行的钩子恰好执行 1 次;afterInsert / record-change 流程触发器行为保持不变(仍恰 1 次)。
需要评估的点:
验收标准
—— #3152 的根治跟进;缓解措施已随 PR #3161 合入。
背景
#3152 在 PR #3161 中按"缓解而非根治"落地:
engine.insertJSDoc 明确 beforeInsert 为 at-least-once 语义(钩子需幂等);engine.insert(array)抛错 →bulkWrite降级逐行重跑完整 insert → 好行的 beforeInsert 钩子仍执行 2 次(副作用型钩子:发通知、计数器、外部 API 调用等重复执行)。当时的结论(见 #3161 PR 描述):根治需要引擎级部分成功 API,超出 #3152 范围。本 issue 跟踪该根治。
方向
给批插入路径一个"部分成功"形态,让
bulkWrite不再需要"整批失败 → 重跑整个 insert"这种放大钩子副作用的降级:engine.insert(array, { partial: true })返回 per-row 结果({ok, record | error}[]),校验失败的行不再让整批抛错;好行只走一遍 hooks → 校验 → 驱动写入。engine.insertMany),旧的insert(array)语义保持不变(仍整批原子抛错),调用方(seed/import 的writeBatch闭包)切到新入口。需要评估的点:
bulkWrite侧的适配:有部分成功 API 后,逐行降级路径(writeOne)的适用面收窄为"驱动层批写失败"场景;attempt上下文)、bug(objectql): 汇总重算失败仅 warn——子记录批量插入报成功,父 summary 静默过期且无重试 #3147SummaryRecomputeError的组合行为。验收标准
[{name:'good1'},{slug:'no-name'},{name:'good2'}])中好行 beforeInsert 执行次数为 1;engine-autonumber-batch、seed-loader-retry、import-runner-idempotency)全绿。—— #3152 的根治跟进;缓解措施已随 PR #3161 合入。