如何回滚?

解读

在国内 Rust 面试中,面试官抛出“如何回滚”往往不是在问“git reset --hard”这类版本控制操作,而是考察候选人是否具备线上故障止血版本快速回退的工程化能力。Rust 项目一旦上线,回滚必须满足三个硬性指标:零停机、零数据污染、可灰度。因此回答要围绕“编译期保障 + 运行时灰度 + 数据兼容”三层防线展开,让面试官感受到你既能写“编译通过即正确”的代码,也能在真实事故中十分钟内完成止血

知识点

  1. Cargo 语义化版本与 yank 机制
    Cargo.toml 中版本号遵循 SemVer,yank 可把指定版本标记为“不可再下载”,但已依赖该版本的二进制仍可用,实现“软回滚”。

  2. 条件编译 + feature flag
    使用 #[cfg(feature = "v2")]编译期把新功能隔离成独立 ELF 段;回滚时只需重启进程并关闭 feature,无需重新编译

  3. ABI 稳定与动态链接
    Rust 默认 ABI 不稳定,回滚热升级需借助 C ABI 边界:把易变业务封装成 cdylib,主进程 libloading::Library::new 动态加载;回滚时替换 .so 文件后 SIGHUP 热重启,秒级切换

  4. 数据库向前兼容的“两阶段”迁移
    新增字段必须可空或有默认值;回滚脚本放在 migrations/{down,up}.sql,通过 sqlx migrate revert 一键回退;禁止 DROP COLUMN,用视图屏蔽废弃字段,保证数据零丢失

  5. 蓝绿 / 金丝雀发布
    在 K8s 中利用 Deployment 的 maxUnavailable=0 + readinessProbe,先起 10% 新 Pod,观测 metrics(如 Rust 的 prometheus crate 暴露的 rust_app_panic_total);异常瞬间切回旧 ReplicaSet,30 秒内完成流量归零

  6. 回滚决策指标
    国内大厂 SRE 通用阈值:P99 延迟上涨 20%错误日志条数/分钟 > 前三天均值 3σ 立即触发回滚;Rust 服务可内置 once_cell::sync::Lazy 静态指标,避免重新埋点

答案

线上回滚分三步走,全部脚本化到 make rollback

  1. 止血
    执行 kubectl patch deployment my-rust-svc -p '{"spec":{"template":{"metadata":{"annotations":{"version":"v1.2.3"}}}}}',把流量瞬间指向上一稳定 ReplicaSet;同时 cargo yank --vers 1.2.4 防止新实例拉取问题版本。

  2. 数据回退
    若事故由 1.2.4 的 migration 引起,立刻 sqlx migrate revert --source migrations/ -t 20240512000000,回滚到上一版本 schema;若新加字段被旧代码读取,用 COALESCE(new_col, default) 视图兼容,保证新旧二进制都能跑

  3. 进程重启
    对于使用 feature flag 的静态链接二进制,直接 systemctl restart my-rust-svc;对于 cdylib 热加载方案,执行 pkill -HUP my-rust-svc,主进程收到信号后 Library::close 卸载问题 so,重新加载上一版本 so用户无感知

整套流程通过 GitHub Action 固化:打回滚标签 rollback-1.2.4 后,5 分钟内自动完成

拓展思考

  1. 如果 Rust 服务嵌入在车载终端(OTA 场景),没有 K8s 怎么办?
    可借助 A/B 分区 方案:把新版本写入空闲分区,设置 GRUB 启动一次标志;启动后 30 秒内未收到 MQTT “健康心跳”,Bootloader 自动回退到旧分区,车机永不“变砖”

  2. 当回滚需要降级数据结构(如把 HashMap<u64, Vec<u8>> 换成数组)时,如何保证向前兼容
    在序列化层使用 serde#[serde(other)] 捕获未知字段,回滚脚本先把新格式导出成 JSON,写 Rust 一次性迁移程序把数据转回旧格式,验证 checksum 后再上线旧版本,杜绝数据剪枝

  3. 回滚后如何复盘
    利用 tracing crate 的 Span 把请求链路与版本号绑定,回滚后 24 小时内通过 jaeger 检索 version=1.2.4 的异常链路,定位到具体函数;再写单元测试 #[should_panic] 复现,把事故转成长效防护用例,真正做到“回滚一次,免疫一类”。