执行语义、约束与安全
最后更新:2026年7月21日
本文梳理 Script 在运行时如何工作(顺序、事务、错误、锁),保存时受到哪些静态约束,以及安全模型。语法参见 Statement 目录与值表达式,实战组合参见菜谱。
执行顺序与模式
statements从上到下顺序执行。到达Return时在该处终止。- Sync 在处理请求的路径上执行,Async 在后台执行。 这只是执行位置的区分,无论哪一种,结果都是
Return值(调用响应形态参见 Script 概述的请求与响应、执行模式)。 - 从能力(capability)到模式:如果 statement 树中含有
ExternalIo(Http外部调用)、MediaIngest(Media 文件摄取。fields.file的{ source, encoding },url 与 base64 通用)、LongRunning(大量 Loop 等)中的任意一个,executionMode就会强制为Async。这三者是相互独立的能力,在下面的静态约束中的计数对象各不相同。
执行语义
Guard(前置条件)
没有专用的 guard 语句。用 If 和 then:[Return] 表达。条件违反时返回结果,之后的 statement 不再执行(当然也可以有不带 guard 的 Script)。
{ "type": "If", "condition": { "<": [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] },
"then": [ { "type": "Return", "value": { "ok": false, "reason": "insufficient credit" }, "statusCode": 402 } ] }无事务与 best-effort 补偿
Script 不是事务。 失败时引擎会尝试对目前已完成的操作进行补偿(compensation)并返回错误原因,但存在以下限制(设计上接受的取舍)。
- 撤销删除会创建新的
sys.id,因此原本指向它的引用会失效(创建的回滚很容易,修改则需要 before-image)。 - 外部副作用(
Http)不可逆(已发出的调用与计费无法撤销)。 - 进程崩溃时可能残留未补偿状态(orphan)。
如果确实需要原子性,请由用户用 Script 亲自补偿,或者把不可逆的操作(外部调用等)放到最后。“能连锁执行却无法回滚、看上去却很安全”的顺序最危险。
乐观锁
update/patch 竞争通过 ResourceUpdate 和 ResourcePatch 的 version 来收窄。给出 version(值表达式,Int)后,只有当它与目标当前的 sys.version 一致时才更新,不一致则以版本冲突错误 abort(可用 Try/catch 局部处理)。省略时不做检查,采用 last-write-wins。通常先用 ResourceRead 或 ResourcePageRead 读取,再传入其 sys.version(参见菜谱中的乐观锁 CAS)。
基于 origin 的写入
写入始终反映到 origin(draft),delivery(CDA/ACDA)的暴露由 publish 控制(ResourceCreate/ResourceUpdate/ResourcePatch 的 publish,或 ResourcePublish/ResourceUnpublish)。
什么算失败
- 真正的失败是 statement 运行时错误:
Http的最终 status 大于等于 400(4xx·5xx;ignoreStatusCode: true时不算失败)或超时、响应 body 超过 10MiB、资源操作失败(对象不存在、版本冲突、不支持的运算等)。这类失败会由引擎 abort 并补偿,可用Try/catch/finally局部处理。 Return不是错误,而是正常的提前终止。它不是catch的对象(没有用户 throw 的概念)。- 在
catch内通过/error引用{ message, statement }。
无服务端聚合
没有专用于 count、sum、group-by 的服务端操作。需要通过遍历 ResourcePageRead 并用 SetVar/JsonLogic 计算,因此受限于 fetch 大小和 maxIterations(不适合数百万条的聚合)。
无等待与延迟
Script 中没有 Delay 语句。Script 在请求路径(Sync)或后台(Async)中只执行一次就结束,不会在内部等待或轮询直到外部 job 完成(Async 结果是另一回事。调用方用 202 收到的 requestId 轮询以获取 Return 值)。
静态约束(保存时校验)
以下会在保存(创建/修改)Script 的时刻检查。违反时保存会被拒绝(不是运行时,而是在编写时失败)。
| 约束 | 默认值 |
|---|---|
存在外部 I/O 时 executionMode 为 Async | 不适用 |
禁止在 Loop body 内进行 Http 外部调用与 Media 文件摄取 | 不适用 |
每个定义最多 Http 外部调用 | 3(maxExternalIo) |
每个定义最多 SetVar(含嵌套) | 5(maxSetVar) |
| 每个定义全部 statement 最多(含嵌套) | 15(maxStatements) |
Http.retry 上限 | 2(maxHttpRetry) |
上限可通过服务器配置(weegloo.core.script.*)调整(上表为默认值)。
Media 文件摄取属于
MediaIngest能力,与Http外部调用(ExternalIo)不同,不计入maxExternalIo(3)的上限。 不过Async强制和Loopbody 禁止会与Http完全相同地适用。
时间预算(运行时)
| 模式 | 默认预算 |
|---|---|
| Sync | 10 秒(syncTimeoutMs) |
| Async | 60 秒(asyncTimeoutMs) |
各套餐的数量上限
Script 是 Billable 资源,每个 Organization 的数量按套餐限制。
| Plan | Script 数量 |
|---|---|
| Free | 3 |
| Basic | 10 |
| Pro | 50 |
| Enterprise | 无限制 |
达到上限后,新建 Script 会被拒绝(与其他 Billable 资源同一路径)。
安全模型
secret 请求头
Http.headers 中 secret:true 的项目仅限 CMA(管理员),不会暴露给最终用户(ServiceUser),且仅在发送前才解密。像 LLM API 密钥这样的机密放在这里(即使被打包为 App Bundle,secret 值也会被掩码,不会离开原始 space)。
执行身份与授权
- 执行身份:执行期间所有资源操作都以调用
/execute的用户身份执行。被创建或修改的资源的createdBy/updatedBy即为调用方,createdBy: ":self"作用域也以调用方为基准解析。 - 授权边界有两个,而且运行时不会对每个 statement 重新检查资源权限。
- 编写时(保存):保存 Script 时,会检查作者是否确实拥有这些 statement 所写入的资源与动作权限。只要缺少任意一个,保存就会被拒绝(
WGL403015)。也就是说,包含无权限操作的 Script 从一开始就无法保存。 - 调用时(
/execute):只检查调用方的 Script Execute 权限。没有权限则返回403。通过后,各 statement 的资源权限在运行时不再确认即执行。这与编程中的函数执行权限方式相同。只要有执行函数的权限,其内部各个操作的权限就不会再被询问。
- 编写时(保存):保存 Script 时,会检查作者是否确实拥有这些 statement 所写入的资源与动作权限。只要缺少任意一个,保存就会被拒绝(
- 所有权作用域:
where过滤器中的createdBy: ":self"表示“仅限当前调用方创建的对象”(例如只查询我自己的钱包)。 - 委托的权限(编写注意):把上述两个边界合起来看,Script 的执行等同于受委托作者的权限来执行。调用方只需拥有 Execute 一项即可,Script 内的 statement 会在作者保存时被授权的范围内照常执行。因此,调用方自己无法完成的资源操作也可能通过 Script 发生。授予作者的权限即是该 Script 的影响范围,所以要谨慎设定 Script 中包含的动作。
摘要检查清单
保存前请确认以下几点。
- 存在外部调用(
Http)或 Media 文件摄取时,executionMode为"Async"。 - 没有把外部调用放进
Loopbody 内。 - 外部调用不超过 3 个、
SetVar不超过 5 个、全部 statement 不超过 15 个。 - secret 值仅通过
Http.headers的secret:true放入。 - 尽量把不可逆的操作(外部调用)放到后面。
- 如果担心 update/patch 竞争,就使用
ResourceUpdate或ResourcePatch的version。 - 若要返回结果,已明确指定
Return.value。
相关文档
- 值表达式:值与条件规则。
- Statement 目录:各语句的字段与结果。
- 菜谱:完整示例集合。
- Script 资源与端点:
Script资源结构及/execute等 HTTP 端点。 - Script 概述:顶层结构与执行模式。
