观察类 finding,来自 #5194 / PR #5753 的实现。今天没有用户会撞到 ,记录在案由 PM 定级。
事实(origin/main + PR #5753 )
REAP_BATCH_SIZE (500) × REAP_MAX_BATCHES_PER_SWEEP (20) 的注释说的是「bound one sweep's work」。实际上这个预算是在 batchedReap() 一次调用 内计的,而 reap() 在有租户覆写时会多次 调用它(lifecycle-service.ts:1128 起):
每个声明了 maxAge/expireAfter 覆写的租户一个 pass(organization_id: t.tenantId + 该租户自己的 cutoff);
外加一个全局 pass($or: [{organization_id: {$nin: [...]}}, {organization_id: null}])。
于是 N 个租户覆写 ⇒ 一个对象一轮最多删 (N+1) × 10000 行,而不是注释承诺的 10000。
为什么这不是 #5194 的回归,也不是当下的故障
这是 guard 路径(finding(objectql): registerReapGuard 后注册者静默顶掉前者,且注册表私有 —— 第二个注册方察觉不到自己解除了别人的 guard #5535 /fix(objectql): 同一 object 的多个 reap guard 按交集组合,后注册者不再静默顶掉前者 (#5535) #5708 )原有的性质;PR fix(objectql): bound every lifecycle reap, not just the guarded ones (#5194) #5753 只是把同一条分批路径扩展到了无 guard 对象,未改变预算的计法;
长事务问题没有回来 :每次删除仍是单行 by-id,(N+1) × 10000 是「一轮总工作量」变大,不是「单条语句变长」;
触发前提是租户级 retention 覆写(P4 governance,lifecycle settings namespace),目前没有部署在用;
每轮之间是一小时的 sweep 间隔,工作量再大也不阻塞别的对象(per-object 失败隔离,leg boundary 检查 abort)。
值得记下来的原因
「bound one sweep's work」是文件里写明的承诺,而在租户覆写存在时它字面上不成立 —— 下一个读这段注释的人会按 10000 估算一轮的成本。要么把预算改成跨 pass 共享(一个对象一轮一份 10000,按 pass 依次消耗),要么把注释改成「per (object, scope)」并说明为什么这样是对的。两种都是小改动,但属于语义取舍,不该由本 PR 顺手决定。
PR #5753 的正文里已如实记了这一条。
Found-during: #5194 / PR #5753
观察类 finding,来自 #5194 / PR #5753 的实现。今天没有用户会撞到,记录在案由 PM 定级。
事实(
origin/main+ PR #5753)REAP_BATCH_SIZE (500) × REAP_MAX_BATCHES_PER_SWEEP (20)的注释说的是「bound one sweep's work」。实际上这个预算是在batchedReap()一次调用内计的,而reap()在有租户覆写时会多次调用它(lifecycle-service.ts:1128起):maxAge/expireAfter覆写的租户一个 pass(organization_id: t.tenantId+ 该租户自己的 cutoff);$or: [{organization_id: {$nin: [...]}}, {organization_id: null}])。于是 N 个租户覆写 ⇒ 一个对象一轮最多删 (N+1) × 10000 行,而不是注释承诺的 10000。
为什么这不是 #5194 的回归,也不是当下的故障
lifecyclesettings namespace),目前没有部署在用;值得记下来的原因
「bound one sweep's work」是文件里写明的承诺,而在租户覆写存在时它字面上不成立 —— 下一个读这段注释的人会按 10000 估算一轮的成本。要么把预算改成跨 pass 共享(一个对象一轮一份 10000,按 pass 依次消耗),要么把注释改成「per (object, scope)」并说明为什么这样是对的。两种都是小改动,但属于语义取舍,不该由本 PR 顺手决定。
PR #5753 的正文里已如实记了这一条。
Found-during: #5194 / PR #5753