Skip to content

沙箱三件套(__txBegin)同样不 join ambient 事务 —— #6168 修好了回调面,VM 侧 ctx.api.transaction 仍在事务中再开事务 #6406

Description

@baozhoutao

发现于 #6168 的实施(PD #10 范围外发现,未在该 PR 内修改)。未认领。

事实

#6168 / PR #6403ScopedContext.transaction()(回调式)补上了 ADR-0067 D2 的 ambient join。实施中实读发现:QuickJS 沙箱面的 ctx.api.transaction(fn) 根本不走那个方法,因此不在该修复的覆盖范围内。

packages/runtime/src/sandbox/quickjs-runner.ts:639-660 里,VM 侧的 ctx.api.transaction 是一段 JS 糖,底下驱动三个 host leaf:__txBegin / __txCommit / __txRollback__txBegin(:566-579)调的是离散三件套:

const begin = apiTx?.beginTransaction;
if (typeof begin === 'function') {
  const r = await begin.call(apiTx) ?? null;
  if (r) { txState.api = r.ctx; txState.handle = r.handle; }
}

ScopedContext.beginTransaction()(packages/objectql/src/engine.ts,三件套区)与修复前的回调面一样,没有 ambient 判定:直接 txDriver()driver.beginTransaction()

于是 #6168 描述的两条后果,在 VM 侧 body 上原样存在:

  1. 再要一条连接 —— 单连接池(knex/SQLite)上就是死锁;
  2. 内层由 __txCommit 自行 commit,写入存活过外层回滚 —— 外层 engine.transaction() 回滚后,VM 里写的行仍在,无报错无日志。

为什么不能顺手在 #6168 里一起修

三件套刻意不用 ALS:body 跨很多 host event-loop turn 执行(deferred promise + pump),setImmediate 边界上 AsyncLocalStorage 不存活 —— 这是三件套存在的全部理由,engine.ts 的三件套 TSDoc 与 ADR-0119 的「declared LIMIT」段都写明了。所以 __txBegin 里直接 txStore.getStore() 未必读得到外层 ambient(leaf 在 deferred 里跑,多半已不在那个 ALS 上下文里),不是加一个 if 就能了事。

至少需要决定:

  • ambient 句柄在哪一刻捕获并显式穿进 txState(建 sandbox api 时?还是 hook dispatch 时?);
  • join 之后 __txCommit / __txRollback 必须弃权(不能 commit 自己不拥有的事务),txState 得带 owned 位;
  • body 显式 rollback 一个 joined 事务时的语义(标记外层必回滚?还是报错?)—— 这一条是契约级决定,不是实现细节。

这些都超出 #6168 的范围(该单锁定回调面),故另开。

可达性(诚实说明)

#6168 同款,且更窄一层:需要「沙箱 hook/action body 显式调 ctx.api.transaction()」+「该 body 由一次 engine.transaction() 触发」+「该 body 走 QuickJS 而非进程内」三者同时成立。示例 app 里没有可指的用例。方向上是持久性一类,不是功能一类:失败时看起来一切正常,回滚后留下不该留的行。

注:quickjs-runner.ts:567 已挡住 body 自己的嵌套(nested ctx.api.transaction is not supported),但那只看 txState.open,看不见宿主侧的外层事务。

#6167 的关系

不是同一件事,别合并。#6167同源校验对不可归属句柄弃权(transactionCoversDriverFor 判不了引擎没开过的句柄);本单是嵌套时不 join。两者都落在三件套这片面上,#6167 的收口方向(让句柄属主在 IDataDriver 上可查)不解决本单,反之亦然。

Refs:#6168(回调面已修,PR #6403)、#6167、ADR-0067 D2、ADR-0119 D1 的 declared-LIMIT 段、#5696(owned 信号)。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions