10 KiB
GitHub 同步状态与检查点设计
本文件细化 design.md 的跨服务状态边界;业务行为以同一 change 下的 specs/ 为准,传输字段以 docs/api/github-sync-internal.openapi.yaml 为准。以下名称是设计语义,实施时可映射到现有实体和枚举。
1. 分层状态
| 层 | 建议状态 | 独立语义 |
|---|---|---|
| 来源绑定 | ACTIVE、PAUSED、UNREACHABLE |
决定是否安排后续采集;UNREACHABLE 仍按周期复查。最近一次访问错误单独记录,不能把限流当不可达。 |
| 任务 | QUEUED、LEASED、RETRY_WAIT、PARTIAL、ACQUISITION_COMPLETE、FAILED |
只表达发现或固定 revision 采集进度;ACQUISITION_COMPLETE 不表示摄入、扫描、审核或发布完成。 |
| Skill 路径 | ACCEPTED、UNCHANGED、IGNORED、RETRYABLE_FAILURE、TERMINAL_FAILURE |
ACCEPTED 是 Java 已持久接收包摄入记录。不能由 Go 宣称 UNCHANGED:它只能报告已采集包,Java 比较正式内容指纹后确定。 |
| 包摄入 | RECEIVED、VALIDATING、RETRY_WAIT、VALIDATED、READY_FOR_REVIEW、NO_CHANGE、BLOCKED、FAILED、APPROVED、REJECTED、SUPERSEDED |
每包独立推进;VALIDATED 仅表示归档与内容政策复核通过,尚未创建审核目标。审核决策和目标取代后,摄入状态也结算,避免历史项长期占用积压容量。 |
| 审核目标 | PENDING、APPROVED、REJECTED、SUPERSEDED |
审核状态不复用任务状态;旧快照保留审计,只有最新目标可批准。 |
PAUSED 由管理员显式操作;访问失败不覆盖管理员暂停。UNREACHABLE 的重查约为首次失败后 24 小时、再失败后 3 天、再次失败后每 7 天。来源恢复时重新对账,已发布 Skill 始终按原生命周期保留。仓库/路径忽略是来源绑定的策略状态,命中已有绑定时停止后续同步,但不删除或撤回内容。
反压计数按 (namespace, repository ID, Skill 路径) 去重,只计该坐标当前最新且未解决的摄入、复核/扫描、待审或待管理员处理冲突项;已批准、拒绝、被取代的历史目标不计。全平台计数是各 namespace 当前计数之和。具体高低水位数值单独确定,不把已完成历史记录数量误当积压。
已登记来源在审核未结束时仍按目标周期执行 HEAD_ONLY;观测到新 commit 且容量允许时,重新执行 FULL。现有待审项不阻止新目标取代;正在校验或发布的同仓库包则先完成当前处理,避免重复采集。
待验证仓库线索不计入待审积压,但有独立的候选预算与发现游标;未完成包准入占位也不冒充待审项,Java 在发放新准入时把两者合并判断剩余容量。后台分别显示待审积压、在途准入和延后候选数量。
新包下载前由 Java 原子授予当前租约、commit、路径的采集准入,并把未完成准入占位纳入容量判断;高水位拒绝新的准入,已获准包完成或失败后释放占位。积压期间发现结果仅进入有预算的待验证仓库线索,不建立正式来源或待审版本;已登记仓库的 HEAD_ONLY 观测不增加待审项。到低水位后重新核对当前上游状态,再恢复包准入。
2. 身份与持久记录
| 记录 | 主键或唯一边界 | 保存的事实 |
|---|---|---|
| 来源绑定 | namespace、GitHub repository ID;TEAM 与 @github 可各有绑定 |
入口集合、管理范围、状态、下次运行时间、最近观测/完整处理 commit、可见性默认值 |
| 发现规则与运行 | 规则 ID、版本、运行 ID;运行检查点 | 结构化查询快照、启用状态、候选与延后原因;不把搜索命中直接视为有效 Skill |
| 仓库任务 | 任务 ID;同来源同类型只有一份未完成工作 | 固定 commit、工作模式、到期时间、租约代际、租约截止、路径进度、重试目标 |
| 路径结果 | 任务 ID、固定 commit、规范化 Skill 路径 | 枚举覆盖范围、结果、摘要或错误;同键重复结果应幂等,摘要冲突应拒绝 |
| 包摄入 | 来源绑定、固定 commit、Skill 路径与内容摘要;另存任务回报键 | uploadId、暂存对象引用、大小/摘要、接收时间、摄入状态;不保存预签名下载 URL,同内容不产生第二待审目标 |
| 同步目标 | 来源绑定与 Skill ID,目标内容/合规指纹 | 当前可批准资格、被取代关联、扫描和审核记录;已拒绝相同指纹不反复入队 |
| 凭证 | 凭证 ID、作用域、密钥版本 | 仅 Java 保存密文;任务表、日志、指标、URL 均不保存明文 |
github_source_skill_binding.latest_ingestion_id 保存当前内容目标,不能被同内容路径移动的 NO_CHANGE 观测覆盖。审核结算按持久的 Skill/version、namespace、repository、source 与当前 ingestion 共同验证,不使用可能已移动的旧路径作为唯一定位。新内容目标应将该 Skill 在旧路径下的旧待审摄入一并取代;哈希相同不撤销代际隔离。
路径需统一规范化为仓库内相对 POSIX 路径,拒绝绝对路径、..、空段、反斜杠和非 SKILL.md 根;Java 不信任 Go 给出的路径。GitHub repository ID 是上游身份;owner/name 是可变展示坐标。Hash 用于内容比较及同仓库唯一路径移动判定,不取代来源身份。
3. 领取、续租与重复回报
- Java 定时调度仅创建或合并到期任务;PostgreSQL 原子领取生成新的
leaseGeneration、随机leaseToken和到期时间。Go 只在当前租约内回报。 renew校验任务 ID、代际和令牌后延长截止时间。租约到期或代际变化时旧执行端必须停止上传和回报;Java 返回租约失效,不允许旧结果覆盖新进度。- Go 在仓库任务内固定一个 commit,完整枚举后逐包采集。每次上传授权绑定当前任务、commit、规范化 Skill 路径和单个临时对象。相同路径同摘要的回报重放返回同一摄入 ID;同路径不同摘要是冲突。
- Java 在短事务中保存路径结果、摄入记录与对象引用,再确认包回报。对象内容与摘要的最终校验在后台异步进行,因此“已接收”不代表“可审核”。
- 任务
complete仅在覆盖范围明确后结算。完整枚举且所有应处理路径已被持久接收或明确判为无需采集/终态失败,才可推进lastFullyProcessedCommit;需重试的路径或不完整子树阻止推进。TERMINAL_FAILURE保留可见错误,不自动视为成功同步该路径。 HEAD_ONLY任务只能推进lastObservedCommit。积压下降后重新解析当前 HEAD 并重新建立采集任务,不补采未处理的每个中间 commit。
lastFullyProcessedCommit 表示该固定 revision 的仓库枚举完整且所有应处理路径已有持久采集结论,不表示 Java 校验成功或审核通过。若某个已接收对象在摄入前丢失/过期,Java 将该路径转为需重采集并使该 revision 的完整处理资格失效;在恢复前不得把检查点作为“已同步成功”对外展示。包内容和合规指纹仍由 Java 计算。
4. 失败与补偿
| 情况 | 处理 |
|---|---|
| Go 停机或续租失败 | 租约到期后重派,恢复已持久的路径检查点;未回报包重做。 |
| 单个包失败 | 继续其他路径的采集、Java 校验及独立同步审核;失败路径按可重试类别和原固定 commit 续跑。超过安全上限、symlink、submodule 是明确的包失败,不产生部分版本。终态失败造成的任务 PARTIAL 不单独阻断已接收兄弟包;旧路径未确认时仍禁止自动移动。 |
| GitHub 403/429 或额度耗尽 | 保存资源类别、额度和 retryAt,释放租约;不得通过持续轮换 Token 规避。 |
| 审核积压达到高水位 | 停止授予新包采集准入;已获准包可以结算,仓库任务保存未处理路径进度。发现任务只保存有预算的仓库线索与游标,已登记仓库只检查 HEAD。低水位后按当前 revision 恢复。 |
| 树截断或子树不可枚举 | 完整子树继续;不完整范围记为 INCOMPLETE 并重试,不能据此推断目录删除。 |
| Go 临时磁盘预算不足 | 能清理并分批续跑时保存路径检查点,回报 LOCAL_DISK_BUDGET;当前容量下一个合规包也无法处理时回报 CAPACITY 并等待管理员调整后重试,不立即循环领取,不把来源标为不可达。 |
| 上游仓库或路径消失 | 仓库不可达按复查节奏;完整树中路径消失仅记录来源变化,不删除已发布 Skill。 |
| 暂存对象摘要/大小不符或过期 | Java 隔离该包并记录原因;可重试对象缺失安排固定 commit 重采集;不创建版本。 |
| 内容未变化 | Java 更新检查记录,不重复创建版本或审核项;许可证/NOTICE 变化仍需重新核对合规指纹。 |
| 发现目标与现有 Skill 冲突 | 阻断该包自动绑定,交 namespace 管理员处理;同仓库其他包继续。 |
| 待审期间出现新内容 | 旧目标失去批准资格,最新目标成为唯一可批准项;已发布版本继续可见。 |
已被 Java 接受且尚未消费的暂存对象不能单凭固定 TTL 清理;清理任务要先核对摄入状态,失败清理留补偿记录。任务与摄入、审核分别展示,避免将“采集完成”误读为“已发布”。
5. 上传大小边界
Go 预检当前有效发布限制,当前 application.yml 配置为最多 100 个文件、单文件 10 MiB、包总量 100 MiB,并限制归档上传大小。Go 在受限本地目录逐包打包,上传授权含预期大小、SHA-256、短有效期和唯一对象 key;回报仅包含 uploadId 与完整性信息。Java 用自身存储适配器解析对象并复核实际大小与摘要,不接收 Go 提供的任意外部链接。本地流式入口在接收时执行硬上限。S3/MinIO 预签名 PUT 对 key 与有效期有限制,但不能仅凭签名宣称服务端强制最大上传字节数;首期以 Go 预检、Java 后检、隔离暂存区容量告警及超额对象清理形成边界。若将来需要对象存储在接收前硬拒绝,应另行验证条件上传策略。