生命周期与冷/热轮换

cat 的生命周期状态、冷/热轮换,以及保证销毁/迁移前不丢写的 drain 流程。

核心不变量:数据面不在 guest 里

一个 cat 是一台 Cloud Hypervisor 微虚机,它通过 virtio-fs 挂载一个 dozycat 卷。真正负责"把 writeback 暂存上传到对象存储、维持元数据会话"的,是 宿主机上的 JuiceFS 客户端——不是 guest。这条解耦是整页的基础。

同步永不停。 因为数据面在宿主侧,pausesnapshot 乃至杀掉 VMM 进程都不会中断宿主机的上传。冻结一只 cat 不会冻结它的持久化。

背景概念(node、cat-agent、volume、route)见 概念与组件;稳定路由见 稳定路由

生命周期状态

cat 在以下状态之间转换。cat-agent 驱动转换,dozycat 注册表记录观测状态。

状态含义Cloud Hypervisor 动作吉祥物
RunningvCPU 执行中、内存常驻 RAM已启动的 VM🐱 醒着的猫
Paused(热停 / hot-stop)vCPU 冻结;内存持久化为本地快照(崩溃可恢复,不是仅留 RAM);VMM 保持 paused 以便瞬时恢复ch-remote pause 然后本地 snapshot😴 打盹的猫(亚秒唤醒)
Snapshotted(冷 / cold-stop)内存/设备状态落盘;VMM 进程可退出ch-remote snapshot 然后 shutdown🧊 冰冻的猫
deep-cold(深冷)无进程;可丢弃快照以省存储。请求到达时重新激活(数据由卷恢复)无进程,按需 boot📦 收起来的猫
Terminated不可恢复的拆除;先 drain 保证不丢写drain → shutdown → 释放会话/锁👋 走掉的猫
热停只用 pause。 热停路径是 pause + 本地内存快照——而不是 RAM-only 暂停。内存被持久化到本地快照,宿主机崩溃后仍可恢复。亚秒级唤醒来自 paused 的 VMM;若 VMM 已死,则从本地快照 --restore

冷/热轮换

在规模化场景(一卷一 VM,约 1000 并发、10 万级轮换),cat 在热↔冷之间轮换以省成本:

  • 热启动(hot-start)ch-remote resume。VMM 还活着,内存原封不动——bootid 不变、内存计数器接着走。亚秒级。
  • 冷启动(cold-start):以 --restore 拉起新的 Cloud Hypervisor 进程,重连 virtio-fs,再 resume guest。
  • 深冷激活(deep-cold):无快照可用时全新 boot + 重挂卷。持久数据从 JuiceFS/S3 卷恢复(bootid 改变 = 全新引导)。

xxxxx.dozyfs 的一次请求即可触发激活:热时为毫秒级,深冷时为秒到数十秒。激活只改写绑定行(epoch+1),稳定名字和客户端连接方式始终不变——详见 稳定路由

热度驱动的预热(warmup)

dozycat 解析访问日志、识别热目录,并在 cat 真正跑起来之前把"该热的"焐热。dozycat 注册表保存热度图与 warmup 状态,热度模块据此生成 juicefs warmup 计划,把热目录预拉进本地盘缓存。这样冷/深冷激活后首批读不必全部回源对象存储。

转换契约

转换动作前必须满足数据面行为结果
启动宿主挂载就绪、virtio-fs socket 就绪建立宿主会话;可选 warmupRunning
热停guest sync;pause;内存持久化为本地快照宿主上传继续Paused
热启宿主会话本就活着Running
冷停guest sync;quiesce 写入;pause;snapshotVMM 退出后宿主上传继续Snapshotted
冷启快照文件存在;virtio-fs 可重连宿主会话延续或重建Running
终止quiesce + 干净 drain暂存清零后释放会话Terminated

Drain 运行手册:销毁/迁移前不丢写

启用 --writeback 时,写入先落到本地 <cache-dir>/<UUID>/rawstaging/,上传到对象存储是异步的。这意味着 guest 的 sync/fsync 并不能证明每个暂存块都已抵达对象存储——它只把 guest 脏页推过宿主文件系统边界。

真正可信的持久化信号是:宿主 JuiceFS 挂载报告 juicefs_staging_block_bytes == 0rawstaging 目录树为空,随后干净卸载deploy/dozyfs-drain.sh 实现了这套检查:

dozyfs-drain.sh <mountpoint> <cache-dir>

可选环境变量:

DRAIN_TIMEOUT=540
DRAIN_OK_MARKER=/var/jfsCache/drain-ok

标记文件在开始时被删除,只有在干净 drain + 干净卸载后才写入。超时、强制卸载、目录缺失、命令失败一律返回非零并且不留下干净标记。

终止时的宿主顺序:

  1. 若 VM 在跑,先 quiesce 写入并执行 guest sync
  2. 在宿主运行 dozyfs-drain.sh <mountpoint> <cache-dir>
  3. 必须拿到干净标记(或零退出码)后,才能释放锁/会话、删除本地状态、销毁或迁移 VM。
卸载失败绝不能返回成功。 drain 在暂存清零卸载干净之前不得报成功。若在 flush 完成前卸载/终止,就是数据丢失。强制卸载或缺依赖时不会产生干净标记——这是有意为之。
冷停后同步继续。 即使 VMM 已停,宿主侧暂存仍会继续下降。你可以先冷停一只 cat,再观察它的 rawstaging 字节继续归零——上传不依赖 guest 存活。

验证

所有生命周期与 drain 行为都有回归测试覆盖:

  • 热停/启sync → pause → 本地内存快照;从 paused 的 VMM resume(VMM 已死则从本地快照 restore)。bootid 不变证明内存确实保留。
  • 冷停/启:sync → pause → snapshot → shutdown → restore → resume;KV 字节级一致,bootid 改变证明数据来自卷而非 RAM。
  • 干净 drain:写入 → drain → 校验标记 → 重挂 → 校验内容。
  • 积压处理:制造暂存积压,drain 阻塞直到清零,或超时返回非零。
  • 失败路径:强制卸载或移除依赖,确认产生干净标记。

真机验收(需 /dev/kvm、Cloud Hypervisor、virtiofsd、JuiceFS/S3)已在 AWS c5.metal 上跑通:多 cat 同台启动、逐 cat 隔离、热停保 RAM、冷停保 KV 字节级一致、深冷靠 boot+重挂卷恢复。源码见 github.com/dozycat/dozyfs