Skip to content

refactor: realign update flow with Moder.Update incremental chain model #322

Description

@ModerRAS

TL;DR

当前 TelegramSearchBot 的更新集成只用了 Moder.Update 的包格式和外部 updater,可是没有真正使用它的链式更新模型。结果是:发布侧每次只生成一个单 entry 的全量替换包,运行时最多只吃一个 entry,B2 又会把历史链条持续剪掉,最后整个系统退化成“远程全量替换分发器”。

目标不是做极致二进制 diff,而是恢复 Moder.Update 设计时的目标:在有限存储内保留足够长的增量更新链,并通过周期性 cumulative/anchor 包控制链长度和体积。


问题总结

当前偏差

  1. TelegramSearchBot.UpdateBuilder 每次只生成一个 catalog entry
  2. 该 entry 基本是当前版本的全量替换包,而不是基于上一个版本的 step package
  3. 运行时没有按 Moder.Update 的完整路径规划语义工作,只是匹配一个可用 entry
  4. push.yml 当前上传完后会把历史包版本 aggressively prune,导致更新链无法长期保留
  5. 包命名仅包含 target version,无法自然表示多来源区间包

结果

  • 失去真正的链式增量更新能力
  • 存储策略退化成“最新包 + 少量残留历史版本”
  • 运行时无法沿 retained chain 逐步升级
  • Moder.Update 的 UpdateChecker / PlanUpdatePath / cumulative preference 设计没有被真正用起来

目标设计

设计原则

  1. 每个版本默认生成 step package

    • 表示 previous -> current
    • 只包含变更文件,而不是整个 standalone 目录
  2. 周期性生成 cumulative/anchor package

    • 用于压缩链深度,避免客户端必须连续下载过多 step 包
    • 不追求极致 diff,而追求“链够长、包数受控、总体存储可界定”
  3. catalog 保留多 entry,支持真实路径规划

    • latestVersion 指向最新版本
    • entries 保留 step / cumulative / anchor 形成的有效更新图
    • minRequiredVersion 与保留窗口同步变化
  4. 运行时按完整路径顺序应用多个包

    • 不是只选一个 entry
    • 应恢复 Moder.Update 的链式更新语义
  5. 清理策略按可达性做,而不是按“只保留最新”做

    • 只有在某个包已经不再属于受支持升级图时才允许清理

推荐修正方案

1. Builder 重构

TelegramSearchBot.UpdateBuilder 需要从“单次打包当前目录”升级为“基于前序版本元数据生成更新图 entry”。

Builder 应具备的新能力

  • 读取前一版或前几版的 release metadata / catalog
  • 比较 previous standalonecurrent standalone
  • 生成 previous -> currentstep package
  • 只把变更文件写入包
  • 在达到阈值时生成 anchor -> currentcumulative package
  • 输出多 entry catalog fragment,而不是单 entry catalog

包命名建议

  • Step: packages/update-<from>-to-<to>.zst
  • Cumulative: packages/update-<anchor>-to-<to>-cumulative.zst

这样可以自然支持:

  • 1.0 -> 1.1
  • 1.1 -> 1.2
  • 1.0 -> 1.2 cumulative

不会再被“同一 target version 只能有一个包”限制住。


2. Catalog 模型调整

发布时不再覆盖成只有当前版本的单 entry catalog,而是维护一份合并后的 retained catalog

Catalog 需要满足

  • 保留所有当前支持升级路径所需的 entry
  • latestVersion 始终指向最新版本
  • minRequiredVersion 指向保留窗口中最老仍支持自动升级的版本
  • entries 中同时存在:
    • 最近 step 包
    • 当前 anchor 窗口对应 cumulative 包
    • 必要时保留上一 anchor 窗口

3. Runtime 接入修正

SelfUpdateBootstrap.Windows.cs 需要从自定义单 entry 逻辑改成真正的路径规划和顺序应用。

现状问题

当前逻辑本质上是:

  • 拉 catalog
  • 找一个当前版本可应用的 entry
  • 下载并准备这一个包

这没有发挥 Moder.Update 的完整设计。

目标行为

  • 获取 merged catalog
  • 基于当前版本和 latestVersion 规划完整 update path
  • 顺序下载并应用 path 中的多个包
  • 优先选择 cumulative package(由 catalog 决定)
  • below minRequiredVersion 明确返回“需要重装”

可以继续保留 bridge -> managed install 的大方向,但 managed install 内的升级语义必须恢复成真正的链式更新。


4. Release Workflow 重构

.github/workflows/push.yml 需要改成“先合并旧状态,再发布新状态,再按可达性清理”。

新发布流程建议

  1. 读取远端现有 catalog.json
  2. 下载当前保留窗口所需的 metadata(必要时)
  3. 构建本次 standalone
  4. 生成:
    • previous -> current step package
    • 可选 anchor -> current cumulative package
  5. 合并 catalog
  6. 先上传新 packages
  7. 再上传新的 catalog.jsonmoder_update_updater.exe
  8. 最后 prune 不可达旧包

关键点

  • prune 必须基于 retained graph,而不是简单“每个文件只保留最新版本”
  • GitHub Release 全量包仍然可以保留,作为人工恢复和回滚手段
  • B2 上的更新源应以“自动升级可达性”为第一约束

5. Bridge 与恢复策略

bridge 不应该再承担主增量链的一部分,而是作为 bootstrap / reinstall fallback。

建议规则

  • managed install 才是主更新目标
  • bridge 主要负责:
    • 首次导入 managed install
    • below minRequiredVersion 时的重装入口
    • managed install 明显损坏时的恢复入口
  • bridge 版本可以独立演进,但不应绑死主更新链的 retention 策略

推荐保留策略

这是建议默认值,后续可再调。

包生成策略

  • 每个版本都生成 step package
  • 满足以下任一条件时额外生成 cumulative package:
    1. 自上个 anchor 以来已累计 5 个版本
    2. 自上个 anchor 以来所有 step 包总大小 > 当前完整包大小的 1.5 倍

保留窗口策略

  • 保留:
    • 当前 anchor 窗口
    • 上一个 anchor 窗口
  • minRequiredVersion 指向当前仍被支持自动升级的最老 anchor 起点

删除策略

仅删除:

  • 不在当前保留窗口内
  • 且不属于任何受支持版本到 latestVersion 的可达路径
  • 且不再被当前 cumulative 包覆盖策略需要的 package

这样能保证:

  • 链长度有上限
  • 存储不会无限膨胀
  • 自动更新路径始终存在

Ralph Loop Task List

以下按 Observe → Reflect → Plan → Act → Assess 拆分。

Observe

observe-update-surfaces

盘点当前 builder、workflow、catalog、bridge、runtime 的实际行为,并明确以下兼容边界:

  • %LOCALAPPDATA%\TelegramSearchBot\app managed install 目录不变
  • moder_update_updater.exe 启动契约不变
  • bridge 到 managed install 的入口仍保留
  • below minRequiredVersion => reinstall required 语义保留

产出: 当前更新面清单 + 兼容性边界文档


Reflect

define-chain-policy

定义链策略与保留策略:

  • step 包生成节奏
  • cumulative/anchor 包生成阈值
  • 包命名规则
  • minRequiredVersion 更新规则
  • prune 的可达性判定规则

产出: 更新图保留策略文档


Plan

redesign-update-builder

重构 TelegramSearchBot.UpdateBuilder

  • 输入上一版本 metadata/catalog
  • 输出 changed-file step package
  • 可选输出 cumulative package
  • 生成多 entry catalog fragment
  • 生成稳定区间命名的 package path

产出: 新 builder 设计与实现

redesign-release-workflow

重构 push.yml

  • 拉取旧 catalog
  • 生成并合并 retained catalog
  • 先上传 packages
  • 后上传 catalog / updater
  • 按 retained graph prune 无效包

产出: 新发布流水线


Act

integrate-moder-update-runtime

修正运行时接入:

  • 使用完整路径规划语义
  • 顺序应用 update path 中多个包
  • 支持 cumulative package 优先
  • 对无路径 / 需重装状态返回明确结果

产出: 真正可走长链的运行时更新逻辑

harden-bridge-and-recovery

加固 bridge 与恢复逻辑:

  • bridge 刷新策略
  • managed install handoff fallback
  • reinstall-required UX
  • managed install 损坏时恢复路径

产出: 更稳的桥接与恢复策略


Assess

validate-update-chain

覆盖验证与文档:

  • 多步升级测试
  • cumulative package preference 测试
  • prune 安全性测试
  • rollback / updater failure 验证
  • 用户文档与运维文档更新

产出: 可验证的长链增量更新方案


成功标准

当以下条件都满足时,说明修正完成:

  1. 新版本发布后,catalog 中存在多 entry,而不是单 entry
  2. 从保留窗口内任意受支持旧版本到最新版本都能规划出 update path
  3. 大多数日常发布只新增一个 step package;必要时新增一个 cumulative package
  4. B2 清理不会破坏任何受支持版本的自动升级路径
  5. runtime 可以顺序应用多个 package,而不是只应用一个
  6. minRequiredVersion 以下的版本会被明确要求重装,而不是进入错误路径

备注

这次修正的目标不是引入极致 binary diff,而是把 Moder.Update 原本设计好的“链式增量 + 周期性累计包 + 有界存储”真正用起来。

Metadata

Metadata

Assignees

No one assigned

    Labels

    ai-task触发ai来工作的标签

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions