mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 06:34:54 +08:00
fix(release): 收紧分发热路径与事务边界
This commit is contained in:
@@ -243,7 +243,7 @@ trusted 和 release/TextUnit/provider/run provenance;`translation.tasks` 优
|
||||
12. 将 staging rename 为 `<output>/versions/<id>`,再原子替换 `<output>/current` symlink 指向该 versioned 目录。
|
||||
13. 发布完成后先对比上一完整 release 和当前 release 的 `official-download-manifest.json`,写出 `official-resource-changes.json` 和 `crowdin-translation-handoff.json`。同一 destination 只有 size 或 BLAKE3 变化才算 modified;新增+变更资源进入解析/翻译 handoff,删除资源只进入差异记录。当前只预留 Crowdin 本地 handoff,不发外部 API 请求。
|
||||
14. 随后刷新 active release 下的 `official-parse-cache.json` 和 `official-textunit-index.json`,并从 Added/Modified 资源、parse cache 与 TextUnit 明细索引派生 `official-textunit-tasks.json`、`crowdin-textunit-queue.json` 和版本化的 `translation-tasks.sqlite`;up-to-date 轮询在已有有效解析缓存、TextUnit 明细索引和 TextUnit 队列时只读取摘要,不重复解析,重新同步队列时保留已有 worker 状态。历史 release 复用只允许不可变资源 payload/sidecar 硬链接;download manifest、snapshot、parse/textunit cache、queue、handoff、bootstrap、CAS reuse references 以及 `translation-tasks.sqlite`、WAL/SHM 都必须独立复制,不能共享可变 inode。
|
||||
15. 若启用 `--import-repository`,已校验 release 会被导入 CAS + `ResourceRepository`,并可经 `resource.index` 查询。历史 release 候选失效时,已有 CAS 对象会先经过完整性和元数据校验,再增加 release 引用并原子物化;当前 release 在 `official-cas-reuse-references.json` 中记录引用,staging/release 清理时递减,失败则回退网络并保留诊断。
|
||||
15. 若启用 `--import-repository`,已校验 release 会被导入 CAS + `ResourceRepository`,并可经 `resource.index` 查询。历史 release 候选失效时,已有 CAS 对象会先经过完整性和元数据校验,再增加 release 引用并原子物化;当前 release 在 `official-cas-reuse-references.json` 中记录引用和首次生成后持久化的 `ownership_id`,staging/release 清理时按 `ownership_id + ordinal` 递减,旧无 identity 清单走明确 legacy key 路径,失败则回退网络并保留诊断。
|
||||
16. 官方同步报告默认给出 `localized_release_status=not_localized`,表示原版资源已发布、汉化资源未发布;generic manifest 驱动的 Binary/JSON/Text 以及当前支持的 UnityFS TextAsset、TypeTree string field 和 managed-reference string field patch 发布成功并通过 `localized-patch-manifest.json`、current symlink、release ID 及 ZIP 内层最终重解析校验后,`localized.status` 才返回 `localized`,表示原版和汉化两套资源都已发布。`localized.status` 分开返回 `patch_manifest_contract_status` 与 `artifact_integrity_status`;state/current/identity 存在但文件被截断或手工修改时返回 `localized.degraded`,只读检查不回滚、不删除、不修复。`translation.proofread` 只会把 workflow 标记成 `manual_proofreading` / `translation.manual_proofreading`,不会回退已发布汉化 release 的发布状态。
|
||||
|
||||
维护期特殊分支:如果官方 launcher/server-info 已经指向新资源根,但 client-patch seed marker 或必需 seed catalog 仍返回 403/404 等未开放状态,`bat` 返回 `waiting_for_official_resources`,保留现有 `current`,不创建失败 staging;若本轮启用 `--auto-discover`,会在 `<output>/official-launcher-bootstrap.pending.json` 写入待处理 launcher bootstrap 证据,供后续排障和自研客户端开发使用。
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
crowdin-textunit-queue.json # Crowdin worker 离线输入队列
|
||||
official-sync-snapshot.json # 常在 active root / current 下
|
||||
official-launcher-bootstrap.json # 官方 launcher 引导链版本化产物
|
||||
official-cas-reuse-references.json # 当前 release 获取的 CAS 引用
|
||||
official-cas-reuse-references.json # 当前 release 获取的 CAS 引用和 ownership_id
|
||||
prod-clientpatch.bluearchiveyostar.com/
|
||||
<root_token>/
|
||||
TableBundles/
|
||||
@@ -110,7 +110,10 @@ manifest 做轻量选择,HTTP 热路径不重新执行完整 release audit;l
|
||||
|
||||
localized publish/rollback 先取得 `.localized-release.lock`,并在 output root 下记录
|
||||
`.localized-transaction.json`。current、version-state 和 version 目录的切换按日志阶段
|
||||
推进;下一次写操作会先恢复或完成未决事务,避免跨进程并发写入和中断后留下半发布状态。
|
||||
推进;publish 只有最终 `verified` phase 才能 roll-forward,下一次写操作会先恢复或完成
|
||||
未决事务,避免跨进程并发写入和中断后留下半发布状态。`release.cleanup execute` 先取得
|
||||
同一 official `.official-sync.lock`,再按固定顺序取得 localized 锁并在持锁状态下重建
|
||||
计划;dry-run 不占用 official mutation lock。
|
||||
|
||||
`release.cleanup` 先生成 dry-run 计划和 `plan_id`,执行时重新计算并比对计划。current、
|
||||
rollback previous、active/in-progress、localized source official、state/manifest/CAS
|
||||
@@ -132,9 +135,12 @@ reference、无法确认 ownership 的对象均保留;只删除重新验证后
|
||||
跨文件系统时复制到 staging 内的临时文件并原子 rename,旧 `versions/<id>` 目录
|
||||
保持不可变。
|
||||
|
||||
从 CAS 物化资源时,`official-cas-reuse-references.json` 记录每个获取的对象引用,
|
||||
文件带版本字段且允许重复 object ID。孤儿 staging 或显式 release 清理必须先按
|
||||
清单减少 CAS 引用,再删除目录;CAS 对象损坏、缺失或元数据不一致时只产生诊断,
|
||||
从 CAS 物化资源时,`official-cas-reuse-references.json` 首次创建时生成并持久化
|
||||
`ownership_id`,记录每个获取的对象引用;文件带版本字段且允许重复 object ID。
|
||||
没有 `ownership_id` 的旧清单保留 legacy ownership key,不在读取时随机迁移。
|
||||
孤儿 staging 或显式 release 清理必须先按
|
||||
清单减少 CAS 引用,再删除目录;cleanup execute 与官方同步共用
|
||||
`.official-sync.lock`,localized cleanup 使用 `.localized-release.lock`。CAS 对象损坏、缺失或元数据不一致时只产生诊断,
|
||||
回退网络下载,不发布未经校验的文件。
|
||||
|
||||
---
|
||||
|
||||
@@ -185,7 +185,7 @@ cargo run -p bat-infrastructure --example official_pull_plan -- \
|
||||
- 新下载先写 `.part`,成功并通过必要校验后再替换为最终文件;如果断点续传后的 `.zip` 结构校验失败,会删除 `.part` 并重新全量下载
|
||||
- 如果上一轮非 dry-run 已进入 staging 但未发布成功,下一轮会优先查找 `<output>/official-version-state.json` 中同一 app version、bundle version 和 Addressables root 的失败版本;只有对应 `<output>/.staging/<id>` 仍存在、路径安全且 `versions/<id>` 尚未发布时,才复用该 staging,并继续按 manifest 校验复用或重下单个 URL
|
||||
- 新 release 的 staging 在访问网络前会扫描已发布 release 的 `official-download-manifest.json`。候选必须同时满足 manifest 记录的 destination、size、BLAKE3 和适用的 ZIP 结构校验;URL、CDN 根和 release ID 的变化本身不会阻止复用。命中后优先用硬链接,跨文件系统时回退为临时文件复制并原子 rename,旧 release 不会被修改
|
||||
- 历史 release 候选失效时,如果配置的 CAS 根已有对应 BLAKE3 对象,会先通过 CAS 读取完整性和元数据,再增加当前 release 的引用并原子物化;当前 release 会写 `official-cas-reuse-references.json`,清理孤儿 staging 或显式清理 release 时递减这些引用。CAS 损坏、缺对象或元数据不一致会写入复用诊断并继续走网络下载,不会静默使用缓存
|
||||
- 历史 release 候选失效时,如果配置的 CAS 根已有对应 BLAKE3 对象,会先通过 CAS 读取完整性和元数据,再增加当前 release 的引用并原子物化;当前 release 会写带持久化 `ownership_id` 的 `official-cas-reuse-references.json`,清理孤儿 staging 或显式清理 release 时按 ownership 和 ordinal 递减这些引用;旧无 identity 清单保留 legacy cleanup key。CAS 损坏、缺对象或元数据不一致会写入复用诊断并继续走网络下载,不会静默使用缓存
|
||||
- 把结果发布到 `--output/current`
|
||||
|
||||
## 5. 自动更新检查
|
||||
@@ -229,7 +229,7 @@ cargo run -p bat-infrastructure --example official_pull_plan -- \
|
||||
- `<output>/official-bootstrap-cache.json`:`--auto-discover` 的 `GameMainConfig` 解析缓存。launcher metadata 与 remote manifest 文件列表 digest 都未变时复用缓存;任一变化时才通过官方 HTTP 按 manifest 下载必要 `resources.assets` 或旧版 game zip 到临时目录解析。
|
||||
- `<output>/official-version-state.json`:资源发布根目录的持久版本状态,包含当前已完成版本、正在拉取版本、上一个可用版本和失败版本。
|
||||
- `<output>/current/official-download-manifest.json`:本地下载强校验清单,记录 URL、相对路径、size 和 BLAKE3。
|
||||
- `<output>/current/official-cas-reuse-references.json`:当前 release 获取的 CAS 引用清单;每个复用项占一条记录,release 清理或孤儿 staging GC 时据此递减引用。
|
||||
- `<output>/current/official-cas-reuse-references.json`:当前 release 获取的 CAS 引用清单,首次创建时包含持久化 `ownership_id`;每个复用项占一条记录,release 清理或孤儿 staging GC 时据此按 ownership/ordinal 递减引用。
|
||||
- `<output>/current/official-resource-changes.json`:当前 release 相对上一完整 release 的资源差异,记录新增、变更、删除以及解析/翻译候选计数。
|
||||
- `<output>/current/crowdin-translation-handoff.json`:为后续 Crowdin worker 预留的本地队列,只包含新增+变更资源;它不是 Crowdin API 调用结果。
|
||||
- `<output>/current/official-parse-cache.json`:官方资源发布后的派生解析缓存,记录 bundle/zip 条目解析摘要和缓存复用情况;它不是汉化产物。
|
||||
@@ -326,7 +326,7 @@ cargo run -p bat-infrastructure --bin bat -- \
|
||||
|
||||
默认平台是 `Windows,Android`,无需显式传 `--platforms`;只有要覆盖默认平台时才传。`--interval` 是正常检查周期,默认 `1h`;watch/daemon 模式还会在每天北京时间(UTC+8)`03:00`、`16:00`、`18:00` 强制执行一次自动刷新,该轮会注入 `force=true`,并且会中断普通 interval 的 sleep。`--error-retry` 是下载、发现或校验失败后的重试周期,默认 `60s`,也可以用 `--error-retry-seconds 60`。CLI 默认启动时向 stderr 打印 `BlueArchiveToolkit` ASCII banner,并把阶段进度日志写到 stderr,包括自动发现、proxy、server-info、marker、catalog、audit、download、snapshot 和 publish 阶段;download 阶段会输出已完成计数和单文件开始/完成状态,worker 从共享队列独立领取任务并在完成后立即领取下一项,完成计数保持单调不倒退,最终 report 的 `items` 仍按 pull plan 顺序排列,audit 阶段会输出官方 `.hash`、本地 BLAKE3、需修复项和 ZIP 结构校验结果摘要。daemon 还会写 `bat-events.jsonl` 结构化日志并按大小轮转。命令结果默认以人类可读摘要写到 stdout。需要纯机器输出时加 `--json --no-progress`,需要显式开启进度日志则用 `--progress`;只想关闭横幅但保留日志时可加 `--no-banner`。错误时 stderr 输出 JSON error,watch 模式下错误 JSON 的 `next_retry_seconds` 使用失败重试周期;如果未关闭 progress,错误 JSON 前可能已有 banner 和进度日志。普通错误 exit `1`,资源目录锁冲突 exit `75`,`verify` 或 `doctor` 发现问题也返回非 0。
|
||||
|
||||
生产可以直接运行 `--watch`,也可以用 `--daemon` 后台运行,或者用 systemd service、容器或 Go 进程守护它。cron/systemd timer 仍可调用单次模式,但不再是 Rust 自动更新的唯一方式。下载默认并发 8,可用 `--download-concurrency` / `BAT_DOWNLOAD_CONCURRENCY` 配置为 `1..=256`;worker 动态领取共享 plan,finished 进度即时按完成数统计,发布 report 仍按 plan 顺序。项目是否热更新、热重载或重启进程,由上层业务集成决定。生产官方资源目录应使用独立输出目录,不要指向现有客户端或人工维护的资源目录;上层读取原版资源时应读取 `--output/current`,不要读取 `.staging` 或 `versions` 中未切换的目录。汉化 Patch/导出应写入 `--localized-output`,并保留官方相对目录结构,不能写回 `--output/current`。发布状态分两档:`not_localized` 只发布原版资源、不发布汉化资源;`localized` 发布原版和汉化两套资源。非 dry-run 每轮会创建 `--output/.official-sync.lock`,防止并发写同一官方资源目录;live daemon 还会阻止前台写命令直接修改它正在管理的同一目录。
|
||||
生产可以直接运行 `--watch`,也可以用 `--daemon` 后台运行,或者用 systemd service、容器或 Go 进程守护它。cron/systemd timer 仍可调用单次模式,但不再是 Rust 自动更新的唯一方式。下载默认并发 8,可用 `--download-concurrency` / `BAT_DOWNLOAD_CONCURRENCY` 配置为 `1..=256`;worker 动态领取共享 plan,finished 进度即时按完成数统计,发布 report 仍按 plan 顺序。项目是否热更新、热重载或重启进程,由上层业务集成决定。生产官方资源目录应使用独立输出目录,不要指向现有客户端或人工维护的资源目录;上层读取原版资源时应读取 `--output/current`,不要读取 `.staging` 或 `versions` 中未切换的目录。汉化 Patch/导出应写入 `--localized-output`,并保留官方相对目录结构,不能写回 `--output/current`。发布状态分两档:`not_localized` 只发布原版资源、不发布汉化资源;`localized` 发布原版和汉化两套资源。非 dry-run 每轮会创建 `--output/.official-sync.lock`,防止并发写同一官方资源目录;`release.cleanup` execute 使用同一个锁并在锁内重新生成/校验 `plan_id`,localized cleanup 使用 `.localized-release.lock`;live daemon 还会阻止前台写命令直接修改它正在管理的同一目录。
|
||||
|
||||
需要只做探测时可以加 `--dry-run`。需要关闭本地 audit 或 repair 时可以显式使用 `--no-audit-local` 或 `--no-repair`,但生产同步默认应保持开启。
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ SQLite `ResourceRepository`,索引不存在时返回 `ok=true` 且
|
||||
|---|---|---|---|
|
||||
| `release.status` | 已实现 | `null` | official/localized current、source relation、match、历史 release 和 manifest/artifact/distribution integrity 统一视图。 |
|
||||
| `release.list` | 已实现 | `{ "channel": "official" }` 或 `{ "channel": "localized" }`,可省略 | 对应 namespace 的历史 release 摘要,包含 stable ID、created/published、current pointer、`rollback_available`、lifecycle、`stale`/`damaged`/`referenced`/`unknown`、legacy 和诊断。 |
|
||||
| `release.distribution` | 已实现 | `{ "channel": "official", "release_id": "...", "destination": "...", "offset": 0, "limit": 1000 }`,均可省略 | Rust 选择的 verified `resource_root` 和 download manifest entries;`destination` 用于对单个实际文件重新校验 bytes/BLAKE3;localized 使用发布时生成的实际字节 metadata,不复用 official size/hash;默认 channel 为 official,选择失败返回 `available=false`,不跨 channel fallback。 |
|
||||
| `release.distribution` | 已实现 | `{ "channel": "official", "release_id": "...", "destination": "...", "offset": 0, "limit": 1000 }`,均可省略 | Rust 选择的 verified `resource_root` 和 download manifest entries;有 `destination` 时是 single-entry lookup,响应固定 `total=1, offset=0, limit=1, entries.length=1`,只校验该实际文件的 bytes/BLAKE3;无 `destination` 时保留管理查询分页语义。localized 使用发布时生成的实际字节 metadata,不复用 official size/hash;默认 channel 为 official,选择失败返回 `available=false`,不跨 channel fallback。 |
|
||||
| `release.cleanup` | 已实现 | dry-run `{ "execute": false }`;执行 `{ "execute": true, "plan_id": "..." }` | cleanup plan、candidate/retain reasons、blocking references 和 removed paths;执行前会重新生成并比对 `plan_id`。 |
|
||||
|
||||
`release.status`、`release.list` 和 `release.distribution` 只读现有 official/localized
|
||||
|
||||
@@ -126,11 +126,15 @@ rollback 与 cleanup 保持独立;缺少 generic manifest 的旧 localized rel
|
||||
明确标记 `legacy`/`unknown`,不会被自动重写。
|
||||
|
||||
本轮 P1 一致性修复已完成:CAS repository 的 store/get/reference/GC 使用跨进程操作锁,
|
||||
release-local CAS 引用通过 durable `(release, ordinal)` ownership ledger 幂等释放;官方
|
||||
历史复用只对不可变文件使用 hard link,`translation-tasks.sqlite` 及 WAL/SHM 始终独立
|
||||
复制;localized output 使用单写者锁和事务日志恢复 publish/rollback,并在发布时写入
|
||||
实际 localized bytes/BLAKE3 的 distribution manifest。分发读取只使用轻量发布 metadata,
|
||||
保留 path ownership、symlink 和文件完整性检查。P2 尚未由本轮处理:ResourceRepository
|
||||
release-local CAS 引用通过持久化 `ownership_id + ordinal` ledger 幂等释放;没有
|
||||
`ownership_id` 的旧清单走明确 legacy cleanup path,不随机迁移 ownership。官方历史复用
|
||||
只对不可变文件使用 hard link,`translation-tasks.sqlite` 及 WAL/SHM 始终独立复制;
|
||||
localized output 使用单写者锁和事务日志恢复 publish/rollback,publish 只有最终
|
||||
`verified` phase 才能 roll-forward,并在发布时写入实际 localized bytes/BLAKE3 的
|
||||
distribution manifest。`release.distribution(destination=...)` 是单条 lookup,返回
|
||||
exactly one entry;分发读取只使用轻量发布 metadata,保留 path ownership、symlink 和
|
||||
文件完整性检查;`release.cleanup execute` 与 official sync 共用 `.official-sync.lock`。
|
||||
P2 尚未由本轮处理:ResourceRepository
|
||||
更完整的查询/权限/损坏恢复、模糊 TM、bat.sock peer credential/perms、FFI 生命周期、
|
||||
资源大小/限额与更强的持久化 fsync 语义仍按后续专项推进。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user