Skip to content

Latest commit

 

History

History
217 lines (133 loc) · 28.7 KB

resource.md

File metadata and controls

217 lines (133 loc) · 28.7 KB

Resource:声明、事务与生命周期

资源所有者定义目标与释放条件,Host 统一校验、持久化、查询和执行容量策略。

定义与校验

Resource 用类型、namespace、name 和 uid 表达身份,spec 保存目标,status 保存插件的业务进展。 ResourceDefinition 统一声明 type、spec/status schema、可选 validator、printer、indexes 和 retention。Host 在恢复资源、启动 Controller 前加载声明;未知类型不能写入。进程内插件通过 resources 提供声明,独立 Runner 的 PackageEntry 通过 resourcesUrl 指向默认导出定义数组的纯模块,Host 与 Runner 分别加载同一声明模块。声明模块不导入 Runner 或 Host 实现,不执行 I/O。

资源统一属于插件实例与 namespace,资源和调度身份不携带 sessionId。底座不预注册任何业务资源。每种资源可以没有 Controller,也可以由多个具名 Controller 观察。

所有创建、更新和删除请求都在存储事务内经过 Schema 与 Validator,包括 Agent 工具、Source 与 commit。Schema 的规范化输出作为持久数据;Validator 可读取同一作用域内的事务快照,包含当前 commit 已完成的前序写入。它不能执行异步工作或外部副作用。校验、引用计算或配额失败会回滚整笔写入。

集合值与进程边界

Resource 的 spec/status 可使用 @compforge/ts-stdx/collections 的 KeyedSet<T, K>。 成员身份由 schema 的 key selector 决定;schema.parse 必须同时接受 JSON 成员数组和已经解码的集合,并返回规范化的集合。 需要稳定指纹的字段由 schema 统一排序。底座不猜测 people、receivers 等业务字段,也不传输 selector 函数。

持久化和 IPC 将集合编码为成员数组;读取、分页、事务校验及 Runner 的事件/执行快照通过声明恢复类型。 Merge Patch、JSON Patch(包括 test)操作编码后的 JSON 文档,完成后重新经过 schema。 底座按数据投影比较变化,并递归克隆和冻结集合及成员;只读快照禁止 add/delete/clear。 需要修改时复制集合,再通过 Resource 更新 API 提交;不要直接使用 structuredClone 或 Node 深比较处理含 KeyedSet 的资源。 HTTP 和工具的展示 DTO 仍显式投影为 JSON 数组。

存储准备与启动

ResourceDefinition 注册只建立类型与回调映射,不读取历史对象。Host 在业务初始化和对应 Plugin 激活前显式调用 ResourceStorage.prepare;已有记录的类型在准备完成前拒绝读写,空类型可以直接使用。同一存储的准备任务串行执行,支持取消;某个 Plugin 准备失败不阻止其它 Plugin 独立准备和激活。

查询索引按插件实例、完整 apiVersion/kind 保存准备版本。indexVersion 默认为 1;索引回调或影响索引结果的 schema 规范化规则变化时必须递增。索引名称集合变化同样触发重建。只有缺失或版本变化的类型重建查询索引;开始重建先撤销原版本标记,全部成功后才写入新版本,中断或失败后重新遍历,未完成的索引不标记为就绪。准备过程不改写 Resource 内容、UID 或 resourceVersion。

每次进程启动仍重新确认已注册类型的 released 和引用关系,不凭查询索引版本信任旧 retention 结果。启动先清空持久化 released 投影,准备按类型分批恢复;任何已注册集合尚未准备完成时,GC 暂停回收,避免使用部分引用图。未知类型保持原引用边且不可回收。正常写入仍在同一事务内更新对象、查询索引和 retention。

准备每批最多读取 64 个对象,处理达到 8 ms 后提交当前批次,并通过 event-loop yield 继续;单个回调与 SQLite 语句无法被该预算抢占。日志记录类型、是否重建、处理数量、批次数、总耗时和最大单批耗时。准备失败使对应 Plugin 保持不可用;reconcile 队列全部清空不是 readiness 的前提。

List 与 page 共用 SQL 选择器,先限定插件实例、类型与 namespace 范围,再加载匹配对象;不通过全库解码发现 namespace。List 返回完整结果并保留原排序,分页接口用于有界遍历。仅查询命中记录参与读取校验,List 不承担全库完整性检查。

Controller 的初始发现与 cron 重扫按 live 游标分批读取资源身份,每批最多 64 个,入队后让出 event loop;执行 reconcile 时再读取最新对象。Runner 先建立队列接收 Watch,再在后台扫描;Host 内置 Controller 同样通过已安装的 Watch 与初始扫描衔接。游标之前的新增或更新由 Watch 唤醒,重复 key 由 workqueue 合并;重启从头发现,删除后的 key 在执行时确认不存在。初始发现任务计入 settled 等待,失败后从头重试,同一 Controller 的扫描合并;关闭后停止继续取页和重试。恢复持久化 deadline 只读取身份与时间,不解码 spec/status。Personal 不再为启动唤醒构造 Topic/Work 展示投影;已有资源统一由 Controller 初始发现恢复。

验证见 tests/resource-startup.test.ts,以及 Resource 分页、Controller 和 workqueue 的回归用例。

API 版本与演进

apiVersion 表达资源结构与语义契约,metadata.generation 表达 spec 修订,metadata.resourceVersion 用于并发控制;产品 VERSION 与它们独立。

不兼容的结构或语义调整必须升级受影响 Kind 的 apiVersion,例如 v1alpha1 → v1alpha2。兼容性新增可留在原版本;已有 Condition 含义的改变同样属于契约变更。升版需要同步 Schema、类型声明、引用与消费入口。

当前开发阶段旧版本先不处理:不实现转换、迁移、双读或字段回退,也不自动删除旧记录。存储与查询按 apiVersion 区分,修改版本号不会使旧记录自动变成新版本。未来确需保留旧数据时再单独建设版本转换能力。

Spec、Status 与写入责任

spec 表达期望或输入契约,status 表达组件已观察到的事实。各 Kind 声明字段含义、写入者及允许写入的阶段;多个组件协作时按字段分工,UID 固定资源身份,写入条件表达操作依赖,保留其它组件负责的状态。更新操作按依赖选择局部 Patch、条件 Patch 或显式 CAS,见下节。事件用于唤醒,恢复与协调依赖持久事实。

status 按对象需要组合三类内容:

内容 用途 示例
具体事实与回执 保存当前关系、结果与恢复依据 资源引用、接纳和完成回执
conditions 提供稳定的状态判断 目标是否达成、是否需要介入
调谐进度 支持等待、重试与重启后继续 已观察版本、重试时间、检查进度

详细执行轨迹独立保存,status 不无限追加模型调用、工具输出和每次 Reconcile 日志。防止重复办理所需的接纳与完成回执属于业务事实,不降级为可能丢失的诊断事件。共享片段由对象按语义组合,不要求所有 Kind 继承包含大量可选字段的 BaseStatus。

更新操作与并发

ResourceClient、Host/Runner 协议与 Storage 共同提供以下更新语义。不同组件可并行修改同一 Resource 的不同字段;整份资源版本只在明确依赖完整快照时作为前置条件。

操作 Aide 契约 Kubernetes 对应
replace 校验 UID 和快照 resourceVersion,整体替换 spec/status Update 的乐观锁
patch,type=merge 将 JSON Merge Patch 应用到事务内最新对象;不隐含快照版本条件 JSON Merge Patch;MergeFrom 默认不锁版本
patch,type=json RFC 6902 test 与修改在一个事务内执行 JSON Patch
patch 的 options.resourceVersion 显式启用整份资源乐观锁 MergeFromWithOptimisticLock
patchStatus 仅对 status 应用 Merge Patch;同样接受显式 resourceVersion status 子资源的局部更新
patchMetadata 仅合并 labels/annotations;不隐含快照版本条件 元数据局部 Patch

Merge Patch 递归合并对象,整体替换数组,null 删除字段。{} 不清空已有对象,省略字段也不删除旧值。需要保存 JSON null 或整体替换一个 map 时使用 JSON Patch 的 add/replace。每种 Resource 的 Schema 仍可提供默认值;无效结构拒绝写入。

// 调度字段已变化也不会阻塞这项独立赋值。
await resources.patchStatus(snapshot, { progress: "observed" });
// 依赖旧内容的编辑明确声明条件。
await resources.patch(snapshot, { type: "json", value: [
  { op: "test", path: "/status/summary", value: snapshot.status.summary },
  { op: "replace", path: "/status/summary", value: revisedSummary },
] });
// 状态转移若依赖完整快照,显式锁版本。
await resources.patchStatus(snapshot, { phase: "accepted" }, {
  resourceVersion: snapshot.metadata.resourceVersion,
});

普通 Patch 仍校验 UID,不能用旧句柄修改重建后的同名资源。可写路径仅限 spec、status、metadata.labels 和 metadata.annotations;身份、版本、结构 owner、删除标记和 Host 调度字段不可修改。patchStatus 无法越过 status 范围。已进入删除阶段的资源由领域 Validator 决定哪些终止写入仍允许,内容工具拒绝继续编辑。

Storage 在 SQLite 写事务内读取当前对象,在副本上应用 Patch,验证元数据、Schema 和领域约束,再提交版本、索引与容量变化。spec 实际变化推进 generation;有效变更推进 resourceVersion;规范化后的空操作仍执行领域 Validator,核对当前执行身份等事务依赖,但不增加版本、不发送 Watch。Host 私有 control 调度记录不属于 Resource 对象;仅 control 变化时保留对象、版本和索引,不调用领域内容 Validator。显式 Resource 空 Patch 仍执行 Validator。Watch 的前后对象来自真实事务快照。commit 可混合普通与条件 Patch,任何一步失败全部回滚;IPC 使用相同路径和错误名称。

显式版本冲突的 Error.name 是 ResourceVersionConflict,UID 变化是 ResourceIncarnationChanged;JSON test 失败保留 TEST_OPERATION_FAILED。Schema/Validator 错误原样返回。IPC 保留 name、message、stack、cause;跨进程根据 name 分类,不依赖 instanceof。条件失败需要重新读取实际依赖,不能盲目重放失败 test 或重做已发生的外部动作。

资源已存在使用 ResourceAlreadyExists。创建方可读取并核对已有对象后复用;它与版本竞争、业务内容过期不是同一种错误。

retryOnConflict(change) 为需要乐观锁的纯资源读改写提供局部重试:最多 5 次尝试,间隔 10 / 20 / 40 / 80ms。闭包每次重新读取、重新计算,仅按跨 IPC 保留的 ResourceVersionConflict 名称重试;其它错误和重试耗尽原样返回。不能在闭包里调用模型、发送消息或执行外部动作,也不能把调用者已检查的内容版本换成新版。Runner 不特殊处理冲突,未处理错误继续使用原有统一失败退避。

字段、集合与领域依赖

调用方只提交实际变化。条件数组不是自动按 type 合并的 map:Personal 的状态转换辅助函数计算 JSON 差异、显式携带整份快照 CAS,保证事实和派生条件一致,冲突后由代码重新协调;不会展开旧 status 提交,也不会覆盖其它组件的条件。独立调度赋值直接走底座 patchStatus,不需要重算 Conditions 或锁整份对象;回顾请求在 Input 创建 Validator 中核对当前 Topic 资格,并与自身调度字段原子提交。内容工具走独立条件 Patch,校验旧内容;执行身份和当前祖先链由插件 Validator 在事务内读取。底座不认识 summary、Note 或 Chief。

Aide 对齐 Kubernetes 更新语义 和 controller-runtime 的显式乐观锁。本轮提供 JSON Merge Patch / JSON Patch,不提供 Strategic Merge Patch、Server-Side Apply 或 managedFields。多资源 commit 与跨资源 Validator 是 Aide 的本地事务扩展,不能视为 Kubernetes 的跨对象事务。

验证覆盖普通并发合并、条件失败、原子回滚、UID 替换、元数据校验、无变化通知及真实 Runner IPC,见 tests/resource-patch.test.ts、tests/plugin-runner.test.ts。Personal 的内容与独立 Note 契约见 Note。

Conditions 契约

需要稳定状态判断的 Kind 可声明 conditions。集合按 type 唯一,每种条件有明确的维护者和业务含义;Host 不要求所有资源具有 conditions,也不将所有条件取 AND 作为通用 Ready。

字段 契约
type 稳定的业务判断名称,含义属于 API 契约
status True、False 或 Unknown;缺失条件表示尚无判断,不能推断为成功
reason / message reason 是稳定机器分类,message 说明当前依据或问题
lastTransitionTime 仅在 status 改变时更新;reason、message 或观察版本变化不刷新它
observedGeneration 判断依据 spec 时记录对应的 metadata.generation

当前实现中,组件在提交前合并自己负责的条件与具体事实,保留其它条件,通过 ResourceClient 的版本校验和事务一起提交;局部条件更新的演进遵循上面的更新契约。Host 校验并持久化调用方提供的状态,不推断业务条件,不设置通用派生钩子。发生冲突时重新读取、重新判断并提交;已发生的外部动作不能为重试写入而重复执行。

跨资源的判断保存依赖版本,由 Watch 与 Reconcile 重算;自身 observedGeneration 无法代表外部依赖的新旧。消费者按契约识别过期结论,不能将旧 True 当作对新状态的保证。Conditions 补充具体事实,不代替幂等、并发控制或安全交接,也不自动解决数据兼容。

这与 Kubernetes 的领域边界一致:条件由所属组件构造,存储不推导其业务含义。但更新语义必须区分普通差异 Patch 与显式 MergeFromWithOptimisticLock,不能将 resourceVersion 检查当成所有 Status().Patch 的默认行为。跨对象判断是带依赖依据的最终一致观察,不承诺全局同时更新。

事务与删除

ResourceClient.commit 在同一插件实例的存储事务中组合 create、replace、patch、patchStatus 和 delete。delete 携带资源快照,校验 UID 与 resourceVersion 后提交级联删除请求,最终移除仍走标准 Reconcile / finalize 生命周期。Schema、版本、引用、索引或配额失败会回滚,Watch 只在提交后收到通知。事务可提交资源与幂等回执,但不能包含外部请求;外部动作成功后的写入冲突不能靠重做动作解决。

单次 commit 上限为 100 个写操作,业务不能通过对整棵树空写来把无界依赖集变成写集。Definition Validator 的 context.get(type, uid) 、context.page(type, { limit, matchIndex }) 和 context.list(type, { namespace, matchIndex }) 在当前存储事务中同步读取依赖,保留插件实例与精确 apiVersion/kind 隔离,读取结果是独立快照;list 未指定 namespace 时覆盖本插件该类型的全部 namespace。前序写入和索引变更对后续校验可见,校验失败整体回滚。page 与异步 ResourceReader.page 共用 SQL 查询路径,默认 namespace 为 v1;存在性用 limit=1,阈值用阈值+1,只解码本页对象。Host 提供读取与事务机制,祖先链、责任归属和在途工作等业务约束由插件声明。

资源引用使用精确 UID;跨外部能力等待后应重新读取,避免更新同名替代资源。删除先写 deletionTimestamp,再由 Controller 完成终止处理和 finalize;结构性 owner 关系支持级联删除。

索引

Definition.indexes 声明纯函数 (resource) => string[],可为一个字段产生多个索引值。resources.list(type, { matchIndex: { name, value } }) 查询精确匹配,并保留插件实例、类型及 namespace 隔离。Host 将索引与资源写入同一事务,删除时级联移除,加载定义时重建;未知索引报错,不回退全表扫描。回调不得执行 I/O 或业务动作。

Definition.orderedIndexes 声明 (resource) => { value: string, order: string }[]。value 是精确分区键,order 是领域定义的 UTF-8 二进制排序键(例如 ISO 时间),同一对象每个索引的 value 唯一,名称不能与 indexes 重复。资源写入与有序投影同事务提交,加载定义时重建。通过 page 的 matchIndex 选择有序索引;list 只接受普通索引。

orderedIndexes: {
  recentSubject: note => note.metadata.deletionTimestamp ? []
    : [{ value: note.spec.subject.uid, order: note.status.updatedAt }],
}
// 最近 40 个事项,不先加载全部 Note。
resources.page(NOTE_TYPE, { matchIndex: { name: "recentSubject", value: uid }, limit: 40, direction: "desc" });

这是 Aide SQLite 存储能力,不能等同于 controller-runtime cache 的 List Limit 或 FieldIndexer:后者提供索引匹配,不保证任意排序查询按页有界执行。这里不提供任意 SQL、路径排序或公共 Count/Exists。

分页查询

ResourceReader.page(type, options) 提供有界读取,Host 与 Runner 使用同一契约。options 复用 namespace、includeDescendants、matchLabels 和 matchIndex,consistency 默认为 consistent,可显式选 live;并要求 limit 为 1..1000 的整数;continue 为上一页返回的不透明游标。返回形态为 { items, metadata: { resourceVersion, continue } },continue 为空字符串表示结束。现有 list 返回完整数组;需要限制读取的路径显式使用 page,不能先 list 再 slice。

const query = { namespace: "v1" as const, matchIndex: { name: "topic", value: topicUid }, limit: 41 };
const first = await resources.page(INPUT_TYPE, query);
// 若索引已准确表达待判断集合,41 条足以判断是否超过 40 条;无需精确 count。
if (first.metadata.continue) {
  const next = await resources.page(INPUT_TYPE, { ...query, continue: first.metadata.continue });
}

页面默认按 metadata.namespace、metadata.name 的 UTF-8 二进制顺序排列,不依赖系统 locale。有序索引按 namespace、order、name 排列,name 在同分区中提供唯一 tie-break;direction 默认为 asc,可设为 desc,方向与索引均绑定游标。namespace 默认 v1,includeDescendants 只匹配路径边界内的后代;类型按完整 apiVersion/kind 隔离,查询始终受插件实例限制。标签为 AND 精确匹配,索引须已由 ResourceDefinition 注册。分页不隐含删除过滤,deletionTimestamp 对象仍可列出;需要业务过滤时由领域声明相应索引。

游标绑定插件、资源类型、namespace 范围和规范化后的 selectors;limit 可以在下一页调整。过滤条件不匹配或游标格式错误返回 ResourceContinueInvalid。consistent 模式下,游标签名不可验证(包括 Host 重启)或集合修订变化返回 ResourceExpired;调用方须丢弃已收集的分页结果,从第一页重读。错误名称经过 IPC 保留,日志只记录 namespace、limit 和是否续页,不记录正文、selector 值或游标。

collection resourceVersion 属于同一插件和精确 apiVersion/kind 的整个集合,不是某个对象的 resourceVersion,不能用于对象 CAS。创建、对象内容更新、删除请求、最终删除、容量淘汰和索引重建会推进集合修订;其它 namespace 内同类型对象的变化也会使游标过期。其它插件/类型的写入、无变化 Patch、调度 control 和 LRU 访问时间不会使游标过期。修订与资源写入同事务提交,失败回滚不会失效。

这与 Kubernetes 的 limit/continue 及 ResourceExpired 恢复方式对齐,但不提供 API Server 保留历史快照的能力:Aide 在集合未变化时继续同一修订,一旦变化就明确过期。游标不跨 Host 重启保存,也不持有长事务、无界快照缓存或整个资源列表。活跃集合可能反复过期,调用方应由后续 Reconcile 重试,不能无界热循环。

SQLite 执行 namespace/selector 过滤、稳定排序、keyset 续页和 LIMIT;先取至多 limit+1 个 key,再在同一事务内读取至多 limit 个完整对象。无索引条件时使用 scope/type/namespace/name 表达式索引;有 matchIndex 时使用 resource_page_lookup 覆盖索引,按插件、类型、索引名和值、namespace/name 有序定位资源键;没有标签附加过滤时,无需逐个读取匹配对象的 JSON 或建立临时排序表。索引投影与资源索引同事务维护,已有索引在首次安装时原子回填。不存在 includeDescendants 的全资源枚举。标签等附加过滤仍可能检查超过 limit 个候选,因此不承诺任意 selector 的 SQLite 工作量为 O(limit)。常用 namespace + matchIndex 查询通过有序范围查找限制候选读取;查询计划测试直接捕获生产 SQL,包含实际 ORDER BY 和续页条件。有序索引使用 resource_ordered_lookup 覆盖索引,机制不感知领域排序键的含义;存在性可用 limit=1,阈值判断可用 limit=阈值+1。

验证见 tests/resource-pagination.test.ts(隔离、页边界、读取预算、查询计划、过期与回滚)和 tests/plugin-runner.test.ts(真实 Runner 分页与错误跨进程)。

Resource 容量与释放

Host 的 ResourceStorage 将所有插件 Resource 存入共享 SQLite,以按需读取替代常驻完整快照集合。容量按 Group/Kind 汇总,配额覆盖不同 API 版本、namespace 和插件实例;同时限制单条大小、每种数量/字节以及 Host 总字节。配置见 Resource 容量。

资源所有者通过 ResourceDefinition.retention 定义释放时间和引用的精确 uid,与类型一起注册。默认没有释放时间的资源保持保护;排序策略只决定合格记录的回收次序。类型可显式声明 collectOnRelease(到释放时间即主动清理)、expiresAt(硬到期上限)以及 pressure(low/high/full 档及候选排序时间);插件拥有业务分类,Host 不解释 completedAt/errorAt 等领域字段。需要依赖其它资源判断安全资格的类型可声明 retention.canEvict;底座在实际淘汰的同一事务内传入当前 ResourceValidationContext,返回 false 时跳过候选。该回调纯同步,异常使写入回滚,不能只依赖提前计算的释放时间保证安全。声明中的 owner 也进入引用索引,正在删除的资源交由原有删除流程完成。业务资源的释放条件由所属插件说明。未知 kind 同样参与配额,未声明释放规则时不会自动删除。

每次写入在事务内同步记录、引用索引和回执,并检查硬配额;超限时回滚写入并通知 Host GC,不在写入调用中回收,也不等待空间释放。GC 通知不随写事务回滚撤销。缩小已有记录和提交标准删除标记允许在超配额状态下进行;deleting 对象仍计入真实容量,实际移除后才释放空间。回收流程见 GC。

原生历史与 checkpoint 由 Harness 管理;Operator 可在资源 status.agent 保存下次调用所需的配置名和会话引用,引用计入资源配额。Harness verb 不读写这些字段。Resource 配额不等于进程 RSS 或全部磁盘文件的硬上限。

GC

Host GC 在 Manager 启动时、周期检查和容量压力通知时运行,同一 Host 的重复唤醒合并为一次执行,关闭 Manager 时停止并等待当前批次退出。首先无条件检查显式开启的到期清理。容量达到 lowWatermark(默认 90%)就产生压力通知;先处理已释放候选,再处理 low 档。仍达到 highWatermark(默认 95%)才处理 high 档,仍达到硬容量上限才处理 full 档。每次删除接纳后重查水位,不因低档候选耗尽而越级清理。未显式开启到期或压力清理的类型继续保留原有保护。已进入 deleting 的用量只用于避免重复安排过量回收,不能用于准入。

按 kind 回收先从该 kind 已到释放时间的成员沿 owner UID 找到 root,再按原有顺序分页选择 root;总字节压力允许选择任何 kind。owner children 查询使用索引。每棵候选树在独立短事务内重新检查全部成员的 released、外部引用和 canEvict,校验通过后原子写入整棵树的 deletionTimestamp。任何成员拒绝删除会回滚整棵树的标记,不影响其它候选。

删除标记经已有 Watch 唤醒资源 Controller,成功完成终止 reconcile 后 finalize。没有 Controller 的资源由 GC finalize;插件未激活、恢复中或 Controller 暂停时保留,不把缺失绑定视为无 Controller。重启从持久化删除标记继续处理。每轮也按 owner 索引查找 owner 已消失或进入 deleting 的 dependent,分批补齐删除请求;这是已决定的生命周期传播,不再依赖容量压力或 released。默认容量选择保护未释放子资源;显式 expiresAt 或 pressure 规则可授权丢弃未完成且被引用的对象,不能被保留引用无限延期。整棵树仍逐节点检查各自资格、canEvict 和生命周期可用性,不因父节点可丢弃而旁路未授权子节点。容量 GC 不跨事务保持整棵树的原子移除,而是保证整棵树的删除接纳原子性,之后沿各对象的生命周期完成。

GC 按配置分页,每棵树检查后让出 Host 执行权。released 与 pressure 档/时间投影随对象写入同一事务更新;候选查询走索引,并在删除事务内重新核验当前资格。为约束同步事务,超过 maxTreeSize 的树整棵保留并记录告警,不截断删除。该限制不影响显式删除契约。retention.canEvict 和领域 Validator 仍必须是纯同步、成本可控的检查;节点上限不是任意回调的硬超时保证。

Resource Printer

ResourcePrinter<Spec, Status> 按资源类型提供 describe(resource): string 和 summarize(resource): string,分别生成可读详情和简短概览。它声明在 ResourceDefinition.printer 中,与 Controller 独立,Resource 实例本身仍是可持久化的数据。

两个方法同步处理传入的资源快照,不修改资源、不查询外部系统、不调用模型。拿到 Resource 后,插件通过 await context.printers.describe(resource) / summarize(resource) 获取文本,无需知道具体 Printer。Host 使用 Manager.describeResource(pluginInstanceId, resource) / summarizeResource(...),同样返回 Promise<string>;Runner 通过 IPC 请求 Host 打印,只传输资源快照和文本;Printer 在 Host 同步执行,始终收到快照副本,不重新查询资源。

Printer 按插件实例与 GVK 选择,同一实例不能重复声明同一类型。类型归属由 ResourceRegistry 维护,不能覆盖其它插件的资源类型;声明失败整体回滚,实例关闭或 Runner 失效不撤销定义。没有专用 Printer 时,详情默认使用缩进 JSON,概览默认使用 kind/name。已注册 Printer 的执行失败记录资源身份和方法名,并向调用方返回错误。

公开接口与示例见 aide-plugin,Input / Output 的具体保留与打印规则见 Personal。

持续变化集合的遍历

page(type, { ...selectors, limit, consistency: "live", continue }) 用于后台协调。首批记录当前遍历方向的末端键,后续越过上次键向初始末端前进,返回每页读取时的最新对象;collection resourceVersion 仅描述该页观察值。状态更新、索引变更、删除和自身写入不使游标过期,签名密钥持久保存在同一个资源数据库中,重启可继续;游标仍绑定插件、类型、selectors 和模式。

live 不保证快照、恰好一次或枚举所有并发新增对象:游标之前插入或移入的对象、上界之后新增对象由后续协调处理,同名重建也必须按 UID 验证。消费者持久化检查点前先完成该页幂等动作,失败可重复该页,执行业务动作时重新核验身份和真实依赖。上下界防止已有游标无止境追逐尾部新增;持续在剩余区间插入仍不承诺固定总工作量。该模式是 Aide 的可恢复遍历扩展,不等同于 Kubernetes 保留历史快照的 limit/continue。

live 有序索引的 order 发生变化时,对象可能移过游标并被重读或遗漏;它不是快照,也不是 exactly-once 处理凭据。后台协调必须幂等并有后续重扫;固定集合完整遍历应使用 consistent。

验证见 tests/resource-pagination.test.ts:默认一致性、live 自身写入/删除/重启、上界、选择器绑定及生产 SQL 的覆盖索引查询计划。