# Workspace Transaction Model Perago 的 workspace transaction 是一个由 runtime 执行的 TCC-inspired 发布模型,只适用于需要发布 workspace 变更的可写路径。它把任务函数限制在 attempt-local workspace 内,让 worker runtime 负责 LakeFS staging、发布 fence、目标 branch 更新、Conductor result 和清理。 正式 LakeFS 操作步骤见 [LakeFS 发布协议](../lakefs-publication-protocol.md)。本页说明该模型提供的保证以及不涵盖的范围。 ## 设计目标 Perago 的事务模型解决 workspace task attempt 的发布安全问题: | 目标 | Perago 的做法 | | --- | --- | | 任务函数保持简单 | task body 只接收本机 `Path` 和 typed params,返回 typed result。 | | 未确认写入不直接污染目标 branch | 所有写入先进入 execution-scoped staging branch。 | | stale attempt 不应继续写 LakeFS | runtime 在可写路径的 stage 或 no-op branch relocation 前检查 attempt fence;stage 后、publish 前再检查一次。 | | retry 可以处理一个 abandoned publication | 当 target head 是 input ref 的直接子提交时,runtime 使用 replacement publication。 | | read-only 节点不制造 workspace 历史 | `WorkspaceSpec(read_only=True)` 不检查 HEAD、不 stage、不 publish,也不进入可写路径 attempt fence;output ref 保持 input ref,result 按普通 Conductor worker completion 回写。 | | 可写 no-op 不制造 empty commit | diff 为空时不创建 staging branch;如果需要处理 abandoned publication,则将 target branch relocate 回 input ref。 | | 不确定状态 fail closed | 无法用 Conductor attempt 状态和 LakeFS HEAD 状态解释时,让当前 attempt 失败。 | Perago 不把 workspace transaction 暴露成用户 API。任务作者不需要写 `try`、`confirm` 或 `cancel` 函数,也不需要在业务代码里直接操作 staging branch。 runtime 的 staged reference 必须完整携带 LakeFS repository、staging branch 和 staging commit。Cancel/cleanup、Confirm/publish 和 retry 判断都应由显式 staged reference 与 workspace input 驱动,不能依赖 worker-local mutable state 来补齐 repository identity。 ## TCC 映射 Perago 借用 TCC 的阶段名称,但阶段由 runtime 完成: | 阶段 | Runtime 行为 | 成功产物 | | --- | --- | --- | | Try | 对 diff 非空的可写 attempt,从 input `workspace.ref` 创建本次 execution 专属 staging branch,把本机 workspace 的 `WorkspaceSpec.prefix` 投影同步进去,并提交 staging commit。 | staging commit。 | | Confirm | 通过 attempt fence 和 publish fence 后,按协议 merge staging branch 或 hard-reset / relocate target branch 到 staged commit;可写 no-op 只做 HEAD 检查或必要的 branch relocate。 | target branch 上的 published workspace ref,或 no-op 的 input ref。 | | Cancel | attempt 失败、stale 或完成后清理 staging branch 和本机 attempt-local workspace。 | 清理日志;不会回滚已成功 publish 的目标 branch。 | 其他事务模型的取舍: | 模型 | 取舍 | | --- | --- | | XA | Conductor task completion 与 LakeFS branch 更新不属于同一个 XA resource manager。 | | AT | Perago 没有透明代理业务写入,也没有可自动回滚 LakeFS commit 的 undo log。 | | Saga | Saga 要求业务补偿或恢复动作,和 Perago 让 task body 保持普通 typed Python 函数的目标冲突。 | ## Attempt Fence Attempt fence 防止失效 worker 继续推进 workspace。Perago 在两个位置检查它: 1. task body 和 post guardrails 成功之后、stage 之前。 2. stage 成功之后、publish 之前。 fresh attempt 必须仍然匹配已 poll 到的 Conductor attempt:`status` 是 `IN_PROGRESS`,`workflow_instance_id`、`task_id` 和 `retry_count` 都没有变化。任一条件失败,attempt 返回普通 `FAILED`,runtime 继续尝试清理 staging branch 和本机 workspace。 双 fence 的原因是 stage 本身可能耗时。第一次 fence 避免失效 attempt 上传 staging workspace,或在可写 no-op 路径回拨 target branch;第二次 fence 避免 stage 完成后已经失效的 attempt 继续更新目标 branch。 ## Publish Fence Publish fence 判断目标 branch 当前 head 是否仍可被本次 attempt 推进。MVP 接受两种状态: | 目标 branch 状态 | 行为 | | --- | --- | | current head 等于 input `workspace.ref` | diff 非空时 merge staging branch,允许首次发布;diff 为空时 no-op completion。 | | current head 的直接 parent 等于 input `workspace.ref` | 视为 abandoned publication;diff 非空时使用 replacement publication,diff 为空时 relocate 回 input ref。 | 其他 branch advancement 都会触发 `PublishFenceError`。这通常表示目标 branch 被其他 workflow step、其他 workflow instance、人工写入或更复杂历史推进;runtime 不发布 workspace output,让 Conductor 按普通失败路径处理。 这个 publish fence 是 client-side soft fence。它在 publish 前读取和判断目标 branch head;它不提供 LakeFS server-side compare-and-swap,也不证明严格 exactly-once publication。 ## Metadata 不需要任何 commit metadata。 commit message 可以用于人工排查。发布判断只看 Conductor attempt fence 和 HEAD 状态。 ## 故障与恢复边界 Perago 的 MVP 事务边界是 operationally bounded: | 场景 | 行为 | | --- | --- | | pre guardrail 失败 | 返回 `FAILED_WITH_TERMINAL_ERROR`,不运行 task body,不发布 workspace。 | | task body、post guardrail、download、stage 失败 | 返回普通 `FAILED`,不发布 workspace。 | | read-only task 成功 | 返回 `COMPLETED`,workspace output ref 保持 input ref,不检查 HEAD。 | | 可写 no-op 成功 | 返回 `COMPLETED`,Perago 不会创建 empty commit,workspace output ref 保持 input ref。 | | attempt fence 失败 | 返回普通 `FAILED`,尝试清理 staging 和本机 workspace。 | | publish fence 或 publish 失败 | 返回普通 `FAILED`,不发布 workspace output。 | | cleanup 失败 | 保留原始 result,只写日志。 | | publish 已成功但 worker 死亡 | 不补发旧 completion;由 Conductor timeout/fail/retry,后续 execution 按 LakeFS HEAD 状态决定是否 replacement publish。 | Fail closed 后,让 Conductor 按失败或重试策略推进;需要人工恢复时,从当前 target branch head 发起新的工作流。 ## 运行时假设 这个模型依赖以下运营约束: - workspace 写入 workflow 在定义上保持串行,不允许并行分支同时写同一个 LakeFS target branch。 - 同一时间只有一个活跃 workflow instance 写入给定 workspace branch。 - human、TS、Python 节点都必须遵守同一套 Conductor 权限边界。 - runtime 必须具备执行 target branch merge 和 replacement publish 所需的 LakeFS 权限。 - `PublishBudget` 应使用真实 LakeFS publish 观测值设置 timeout、heartbeat 和 shutdown grace。 更细的执行顺序见 [Workspace Publication](../runtime/workspace-publication.md),LakeFS object 同步规则见 [LakeFS Runtime](../runtime/lakefs.md)。