豫言 / 豫言操作系统持久事务 @ 0.2.0

在同一持久仓原子读取、写入、删除、列举并返回结构化值,也可在普通持久对象事件中直接访问持久仓。

源码浏览已生成;不能解析的文件以纯文本展示,不表示通过类型检查。

规范.汉语.md

# 持久事务接口规范 0.2.0

本包让应用读写当前持久对象(Durable Object)的持久仓,并在同一个原子事务里做多步读写。0.2.0 在 0.1.0 之上**只增函数,旧函数签名不变**:补足事务内的删除、列举与回滚,并让应用在普通持久对象事件(`durable-fetch`、`durable-alarm`、套接字事件等)里直接读写持久仓。仓内每个键的值是一个 JSON 值:宿主以结构化值存入平台存储,因此与既有 JavaScript 对象写入的数据互通——读出时得到该值的 JSON 文字,应用写入 JSON 文字后,旧 JavaScript 代码读到的是同一个对象。仓由当前持久对象独占,宿主不得把另一个对象、另一次事务的状态暴露给调用方。

## 事务

`执行持久事务` 接受有效 JSON 文字,在同一仓内调用应用的事务处理入口(事件种类 `durable-transaction`,应用据[网页入站接口](../网页入站/规范.汉语.md)的事件种类选择处理入口),成功时返回入口设定的 JSON 结果文字。处理入口通过 `读取持久事务参数` 取得本次参数;通过 `读取持久事务键值`、`写入持久事务键值`、`删除持久事务键`、`列举持久事务键值` 读写当前事务的键;通过 `回滚持久事务` 放弃本事务已作的全部写入;`完成持久事务` 每次事务必须且只能成功调用一次。输入 JSON 由宿主验证,输出须为有效 JSON。

所有读写属于同一个原子事务:处理入口抛错、缺少结果、超时或存储失败时,已写入的键、已设置或删除的告警都不得提交。一个仓内并发事务串行执行,且事务处理入口运行期间宿主不向该对象投递其他事件(它们排队等候,等价于[持久独占](../持久独占/规范.汉语.md)),所以事务里可以等待外部输入输出,但应尽量短:平台在事务回调超过 30 秒时会重置整个对象,宿主因而把 `durable-transaction` 事件默认限制在 25 秒内(可配置,不得超过 30 秒),超时表现为 `执行持久事务` 抛出的可捕获异常。`回滚持久事务` 之后不得再读写本事务的键(平台会拒绝这些调用),处理入口应立即 `完成持久事务`。事务处理入口内不能再启动事务或[持久独占](../持久独占/规范.汉语.md)区;在事务处理入口里调用[持久告警](../持久告警/规范.汉语.md)函数时,告警的设置与删除与本事务同命运(事务提交则生效,回滚或失败则撤销)。

`执行持久事务` 失败时(处理入口抛出未捕获异常、没有调用 `完成持久事务`、结果不是有效 JSON、事务超时、存储失败)抛出可用「尝试运行」捕获的豫言异常,消息以 `持久事务失败:` 开头并保留原因文字;所有写入已回滚。处理入口自己抛出而未捕获的豫言异常在宿主边界表现为 `RuntimeError: illegal cast`,宿主会在消息后附上说明;需要向调用者报告具体原因时,处理入口应自己用「尝试运行」捕获,并把原因写进 `完成持久事务` 的结果。

## 直接访问持久仓

`读取持久仓键值`、`写入持久仓键值`、`删除持久仓键`、`列举持久仓键值`、`清空持久仓` 让应用在普通持久对象事件里不开事务直接访问持久仓,常用于只读一个键、写入后立即返回、扫描前缀或清库。它们只在持久对象事件中可用(事件种类以 `durable-` 开头),**不能在 `durable-transaction` 处理入口内调用**(那里的读写必须属于当前事务),在其他事件或事务入口内调用会抛出可捕获异常。单次调用是平台的一次原子存储操作;多次直接调用之间不构成事务,其间对象可能处理其他事件(除非在[持久独占](../持久独占/规范.汉语.md)区内)。

## 函数总表

| 函数 | 使用位置 | 说明 |
| --- | --- | --- |
| `执行持久事务` | 持久对象事件 | 0.1.0;失败改为抛可捕获异常 |
| `读取持久事务参数`、`完成持久事务` | 事务入口 | 0.1.0 |
| `读取持久事务键值`、`写入持久事务键值` | 事务入口 | 0.1.0 |
| `删除持久事务键`、`列举持久事务键值`、`回滚持久事务` | 事务入口 | 0.2.0 新增 |
| `读取持久仓键值`、`写入持久仓键值`、`删除持久仓键`、`列举持久仓键值`、`清空持久仓` | 非事务的持久对象事件 | 0.2.0 新增 |

`读取持久事务键值` 与 `读取持久仓键值` 返回存在标记和值的 JSON 文字:缺键返回阴与空字符串,已存 JSON null 返回阳与 `null`,二者不混淆。`删除持久事务键` 删除一个键(键原本不存在时无事发生);`删除持久仓键` 返回阳表示键原先存在。`列举持久事务键值`、`列举持久仓键值` 接受选项 JSON 对象文字,返回 JSON 数组文字 `[[键,值],…]`,按键的 UTF-8 字节序排列(`reverse` 为真时逆序),**值是存入的 JSON 值本身,不是再编码的字符串**;事务内的列举能看到本事务尚未提交的写入与删除。`清空持久仓` 清空当前对象的全部持久数据(键值;SQLite 存储上还含 SQL 表,平台按兼容日期决定是否连告警一并清除)。

## 键、值与列举选项

键按 UTF-8 计须为 1 至 2048 字节。值是有效 JSON 文字,不超过 2 MiB,嵌套不超过 32 层,且不含宿主保留的单键对象——即只有一个成员且成员名为 `$句柄`、`$未定义`、`$大整数` 或 `$数字` 的对象(这些是宿主与豫言交换特殊值的记号,写入会被拒绝);含其他成员的对象、以及这些名字作为字符串值出现都是允许的。数字沿用 JSON 数字语义,绝对值超过 2^53−1 的整数应用字符串表达。

列举选项是 JSON 对象文字,字段均可省略:`prefix`、`start`(含)、`startAfter`(不含)、`end`(不含)为字符串,空字符串视同未指定;`limit` 为不小于 1 的整数;`reverse` 为布尔。`start` 与 `startAfter` 不能同时给出;出现其他字段、类型不符、`limit` 小于 1 时失败。省略 `limit` 时返回全部匹配项,但结果 JSON 文字超过 8 MiB 时失败,应用应按 `limit` 加 `startAfter` 分页。

## 失败情形

| 情形 | 类别 |
| --- | --- |
| 键长度越界;值不是有效 JSON、超过 2 MiB、嵌套过深或含保留单键对象;列举选项非法;列举结果超过 8 MiB | 违规或超限:抛可捕获的豫言异常,消息为中文原因 |
| 在不允许的事件中调用(事务函数在事务入口外、直接访问函数在事务入口内或非持久对象事件) | 违规:抛可捕获的豫言异常 |
| `执行持久事务` 中处理入口失败、超时或平台报错 | 抛可捕获的豫言异常,写入已回滚 |
| 交换文字超过宿主上限(16 MiB)、存储后端故障 | 平台或宿主故障:中止当前事件,豫言无法捕获 |

## 跨宿主一致性

任何实现须保持:值以 JSON 语义往返(`读取` 得到的文字与 `写入` 的值在 JSON 意义上相等,不承诺字节相同);缺键与 JSON null 可区分;列举的顺序、边界与 `limit` 语义如上;事务原子、事务期间其他事件排队、回滚后写入不可见;上述限额与失败类别。宿主可以选择不同的存储后端,但不得改变这些可观察结果。

## 一致性样例

设仓为空。`写入持久仓键值 "p:a" "1"`、`"p:b" "2"`、`"p:c" "3"` 后:`读取持久仓键值 "p:a"` 得(阳,`1`);`读取持久仓键值 "无"` 得(阴,空);`列举持久仓键值 {"prefix":"p:","limit":2,"reverse":true}` 得 `[["p:c",3],["p:b",2]]`;`列举持久仓键值 {"prefix":"","startAfter":"p:a"}` 得 `[["p:b",2],["p:c",3]]`。写入 `null` 后读取得(阳,`null`)。写入 `{` 或 `{"$句柄":"1"}` 或长度 2049 字节的键抛异常且不改仓。

在事务入口内依次 `删除持久事务键 "p:a"`、`写入持久事务键值 "p:d" "4"`,此时 `列举持久事务键值 {"prefix":"p:"}` 得 `[["p:b",2],["p:c",3],["p:d",4]]`;`完成持久事务` 后仓中同样如此。若在写入 `p:e` 之后调用 `回滚持久事务`,则仓仍为提交前的样子。事务入口抛出未捕获异常时,`执行持久事务` 抛出以 `持久事务失败:` 开头的异常且仓不变。

## 边界

本包不提供 SQL、同步事务、批量读写、`allowConcurrency`、`noCache`、`allowUnconfirmed` 等平台选项,也不提供二进制或非 JSON 结构化克隆值;需要这些的应用另用适配库。既有数据中嵌套超过 32 层的值无法读出,读取它会使宿主中止当前事件。首版借标准库字符串承载 JSON 值,应用可用豫言 `癸象` 在自身逻辑中解析与构造;事务重入 `_start` 是适配实现,后续可换成类型化回调,应用所见的原子性与值语义不变。宿主的事件时限配置(`执行配置.json`)属宿主构建,不属于本接口。本包只用于已授权的当前持久仓,仓的名称与授权由宿主与部署配置决定。

规范.文言.md

# 持久事务之约 0.2.0

此包使客读写当前持久对象(Durable Object)之久仓,并于一原子事务中行多步读写。二版于初版之上**惟增其术,旧术之签不易**:补事务内之删、列与回滚,又使客于寻常持久对象之事(`durable-fetch`、`durable-alarm`、套接字诸事)中,不开事务而径读写久仓。仓中每键之值为一 JSON 值:宿主以结构化值纳于平台之存,故与旧 JavaScript 对象所书之数相通——读出则得其 JSON 文,客书 JSON 文,则旧 JavaScript 读得同一对象。仓惟当前持久对象独有,宿主不得露他对象、他事务之态于调用方。

## 事务

`执行持久事务` 以有效 JSON 文为参,于同仓调用客之事务入口(事类 `durable-transaction`,客循[网页入站之约](../网页入站/规范.文言.md)之事类择入口),成则返入口所定 JSON 果文。入口以 `读取持久事务参数` 取参;以 `读取持久事务键值`、`写入持久事务键值`、`删除持久事务键`、`列举持久事务键值` 读写本事务之键;以 `回滚持久事务` 弃本事务已作之诸写;`完成持久事务` 每事必且惟一成功。输入 JSON 宿主验之,输出须为有效 JSON。

诸读写属同一原子事务:入口抛错、无果、逾时或仓败,则已写之键、已设或已删之告警皆不得提交。同仓并事串行;事务入口运行之际,宿主不向该对象投他事,他事排队以候(等于[持久独占](../持久独占/规范.文言.md)),故事务中可候外物,然宜短:平台于事务回调逾三十秒则重置整个对象,宿主因限 `durable-transaction` 之事默为二十五秒(可配,不得逾三十秒),逾时则 `执行持久事务` 抛可捕之异。`回滚持久事务` 之后不得再读写本事务之键(平台拒之),入口当即 `完成持久事务`。事务入口内不得复启事务或[持久独占](../持久独占/规范.文言.md)之区;于事务入口内用[持久告警](../持久告警/规范.文言.md)之术,则告警之设与删与本事务同命:事务提交则行,回滚或败则撤。

`执行持久事务` 败时(入口抛未捕之异、不曾 `完成持久事务`、果非 JSON、事务逾时、仓败)抛可以「尝试运行」捕之豫言异,辞以 `持久事务失败:` 起而存其因;诸写已回滚。入口自抛而未捕之豫言异,出宿主之界则显为 `RuntimeError: illegal cast`,宿主于辞后附其说;欲告调用者以确因,入口当自以「尝试运行」捕之,而书其因于 `完成持久事务` 之果。

## 直读写久仓

`读取持久仓键值`、`写入持久仓键值`、`删除持久仓键`、`列举持久仓键值`、`清空持久仓` 使客于寻常持久对象之事中不开事务而径行久仓,常用于只读一键、书毕即返、扫前缀或清库。此五术惟持久对象之事(事类以 `durable-` 起)可用,**不得于 `durable-transaction` 入口内用之**(彼处读写必属当前事务),于他事或事务入口内用之,则抛可捕之异。单次调用乃平台一次原子存取;数次直调之间不成事务,其间对象或理他事(惟在[持久独占](../持久独占/规范.文言.md)之区内则否)。

## 术之总表

| 术 | 用处 | 说 |
| --- | --- | --- |
| `执行持久事务` | 持久对象之事 | 初版有之;败则改为抛可捕之异 |
| `读取持久事务参数`、`完成持久事务` | 事务入口 | 初版有之 |
| `读取持久事务键值`、`写入持久事务键值` | 事务入口 | 初版有之 |
| `删除持久事务键`、`列举持久事务键值`、`回滚持久事务` | 事务入口 | 二版新增 |
| `读取持久仓键值`、`写入持久仓键值`、`删除持久仓键`、`列举持久仓键值`、`清空持久仓` | 非事务之持久对象事 | 二版新增 |

`读取持久事务键值` 与 `读取持久仓键值` 返存否之标与值之 JSON 文:缺键为阴与空文,已存 JSON null 为阳与 `null`,二者不混。`删除持久事务键` 去一键(本无则无事);`删除持久仓键` 返阳者,键原有也。`列举持久事务键值`、`列举持久仓键值` 受选项 JSON 对象文,返 JSON 数组文 `[[键,值],…]`,依键之 UTF-8 字节序而列(`reverse` 为真则逆序),**值乃所存 JSON 值本身,非再编之字符串**;事务内之列,得见本事务未提交之写与删。`清空持久仓` 尽清当前对象之久数(键值;SQLite 之存则并及 SQL 表,告警是否同清,平台依兼容日期而定)。

## 键、值与列举之选

键依 UTF-8 计,须一至二千零四十八字节。值为有效 JSON 文,不逾二 MiB,嵌套不逾三十二层,且不含宿主所留之单键对象——谓仅一成员而其名为 `$句柄`、`$未定义`、`$大整数` 或 `$数字` 之对象(此乃宿主与豫言相授异值之记,书之则拒);对象含他成员者,与此数名为字符串值者,皆许。数循 JSON 数之义,绝对值逾 2^53−1 之整数,客当以字符串表之。

列举之选为 JSON 对象文,诸字段皆可省:`prefix`、`start`(含)、`startAfter`(不含)、`end`(不含)为字符串,空串视同未指;`limit` 为不小于一之整数;`reverse` 为布尔。`start` 与 `startAfter` 不得并给;见他字段、型不符、`limit` 小于一,则败。省 `limit` 则返尽数所合,然果 JSON 文逾八 MiB 则败,客当以 `limit` 与 `startAfter` 分页。

## 败之情形

| 情形 | 类 |
| --- | --- |
| 键长越界;值非有效 JSON、逾二 MiB、嵌套过深或含留单键对象;列举之选不合;列举之果逾八 MiB | 违约或逾限:抛可捕之豫言异,辞为中文之因 |
| 于不许之事中调用(事务术在事务入口外,直读写术在事务入口内或非持久对象之事) | 违约:抛可捕之豫言异 |
| `执行持久事务` 中入口败、逾时或平台报误 | 抛可捕之豫言异,诸写已回滚 |
| 所换文逾宿主之限(十六 MiB)、存后端故障 | 平台或宿主之故:止当前事,豫言不可捕 |

## 跨宿主之一致

诸实现须守:值以 JSON 之义往返(`读取` 所得之文与所 `写入` 之值,于 JSON 意义相等,不承字节相同);缺键与 JSON null 可辨;列举之序、界与 `limit` 之义如上;事务原子、事务之际他事排队、回滚之后诸写不可见;上述之限与败类。宿主可择异存后端,然不得易此诸可察之果。

## 一致性样例

设仓空。`写入持久仓键值 "p:a" "1"`、`"p:b" "2"`、`"p:c" "3"` 之后:`读取持久仓键值 "p:a"` 得(阳,`1`);`读取持久仓键值 "无"` 得(阴,空);`列举持久仓键值 {"prefix":"p:","limit":2,"reverse":true}` 得 `[["p:c",3],["p:b",2]]`;`列举持久仓键值 {"prefix":"","startAfter":"p:a"}` 得 `[["p:b",2],["p:c",3]]`。书 `null` 后读之得(阳,`null`)。书 `{`、`{"$句柄":"1"}` 或二千零四十九字节之键,皆抛异而仓不改。

事务入口内依次 `删除持久事务键 "p:a"`、`写入持久事务键值 "p:d" "4"`,此时 `列举持久事务键值 {"prefix":"p:"}` 得 `[["p:b",2],["p:c",3],["p:d",4]]`;`完成持久事务` 后仓中亦如是。若书 `p:e` 之后调 `回滚持久事务`,则仓仍如提交之前。事务入口抛未捕之异,则 `执行持久事务` 抛以 `持久事务失败:` 起之异而仓不变。

## 界

此包不供 SQL、同步事务、批量读写,亦不供 `allowConcurrency`、`noCache`、`allowUnconfirmed` 诸平台之选,二进制与非 JSON 结构化克隆之值亦不供;须此者,客另用适配库。初版借标准库字符串载 JSON 值,客可用豫言 `癸象` 自析自造;事务重入 `_start` 乃适配之术,他日可换类型化回调,原子与值义须存。旧数中嵌套逾三十二层之值不可读出,读之则宿主止当前事。宿主之事时限配置(`执行配置.json`)属宿主之构建,非此接口之事。此包惟用于获授之当前久仓,仓之名与授由宿主及部署之配置定之。