一项持久的存储更改。许多可选的实时迁移。
压缩会证明紧凑 S 层与其替代的原始 L 层区段具有相同的合并视图,然后把它提交到活动 manifest。提交后的清理会尝试释放历史 pin;即使清理失败,也无法撤销存储提交。
交易边界
提交第一;经核实后方可回收。
Before
- L₄·最新
- L₃
- L2
- B₀·基础
已提交head
- L₄·赛车head保留
- S₅·L₃+L2
- B₀·基础
正确性
合并(B₀,L2,L₃,L₄)=合并(B₀,S₅,L₄)
Reclamation
L2 和 L₃ 仅在每个租约释放后才会消失。
昂贵的折叠运行在 写入锁 之外。
租约计划将不可变源层转换为稳定的构建输入。短暂的独占阶段仅重新验证、提升、同步和交换元数据。
- 01简短的共享锁
规划
读取活跃的manifest,收集每个活跃的租约的最新层作为边界,划分至少两个层的最大原始运行,并获得计划租约。
没有基础 B*层且没有活动边界进入块。
- 02无存储锁
建造
将每个选定的最新运行折叠到随机数 暂存 树中。保留 whiteout 和不透明度;硬链接常规文件获胜者并重新创建符号链接。
发布可能会继续,因为计划租约引脚每个源。
- 03独占锁
提交
重新读取 head,要求每个原始运行保持连续,重命名暂存的 S层,同步存储文件系统一次,并自动替换 manifest。
记录替换,然后发布计划租约——该交易的唯一 GC。
竞赛安全 head 保存
由于发布在展平运行时进行前置,因此提交会重新读取最新的 manifest 并仅拼接仍然连续的计划运行。参加比赛的发布仍保留在 head 中。
压缩工作根据访问的元数据而不是基础映像副本进行扩展。
这些界限描述了当前实现的主要工作。 L = 活动层、R = 活动租约、E = 已访问条目、V = 压缩输出条目、P/T/F/M = 过程检查输入、N =会话、W = 扫描宽度。
| 暂存 | 时间 | 额外空间 | 为什么 |
|---|---|---|---|
| 规划 | O(L + R log R) | O(左+右) | 扫描L active层并从R live租约订购最新边界。 |
| Flatten | O(E log E) 最坏情况 | O(V) 条目 | 走 E 源条目并在有序地图中选择获胜者。常规有效负载块由硬链接共享; V 是压缩的可见输出。 |
| 提交 | manifest复查+Q重命名 | O(L+Q) | 针对最新的 manifest 重新验证并拼接 Q 块; syncfs 的持久性通常主导着 wall time。 |
| 租约重写 | 生成×链式扫描 | 奥(大) | 将追加顺序替换应用于一个 已租借 链,然后验证每个重写的层目录是否仍然存在。 |
| Quiesce | O(P+T+F+M) | O(P+T) | 在切换之前扫描 /proc、任务线程、文件描述符和内存映射行。 |
| 会话扫频 | O(N) 功 · ≈⌈N/W⌉ 波 | 在) | 重新挂载N会话,带边界宽度 W。当前默认 W 为 4;结果排序保持稳定。 |
硬链接是常规文件有效负载不复制到 S 中的原因。新树仍然需要目录条目、符号链接、whiteout、不透明标记、xattrs 和 暂存 空间,直到提交。
在旧链条移动之前,需要获取较短的链条。
每个已提交 S 层都会在按追加顺序记录的内存映射中保存其替代的原始区段。重写时先应用较早的代次,使替换可以跨后续压缩继续组合。
追加订单
引脚重叠时间线
不存在未固定的瞬间。
- 1旧租约持有原始层OLDNEW
- 2获取新的替代品租约OLDNEW
- 3静止和开关挂载OLDNEW
- 4验证新的,然后释放旧的OLDNEW
缺少替换
身份;保留现有的租约。
缺少层目录
身份;永远不要构建部分链。
没有更短的链条
身份;重新挂载什么也没有。
停止每个观察者,然后证明它没有工作区引脚。
该交换机在会话闸门内部运行。读取错误是不确定性的,而不是权限:任何子挂载、转义的命名空间、新任务、cwd/root/fd/map pin 或不可分类的状态都会在不返回点之前干净地阻止迁移。
- 01
首先阅读holder mountinfo。工作区必须是 OverlayFS 并且没有子挂载。
- 02
发现 holder挂载命名空间和会话cgroup 中的任务联合。
- 03
SIGSTOP every observable task, then poll every thread until stopped, exited, or safely traced.
- 04
再次发现。任何新任务都意味着成员资格发生变化,因此尝试会停止。
- 05
检查每个冻结线程的挂载命名空间、cwd、根、文件描述符和内存映射。
FrozenTasks 是一个守卫
在干净的跳过和经过验证的开关上,放下防护发送 SIGCONT。在错误的 PONR 后状态下,防护被故意遗忘,因此没有任务观察到部分挂载。
交互式cwd是一个真正的pin
当前目录位于工作区内部的实时 shell 可以预见地阻止重新挂载。会话保持正确,其旧租约仍然具有权威性。
我们可以估计重新挂载时间,但还不是确切的应用程序暂停时间。
保留的走线包裹了完整的会话重新挂载操作。它们建立了空闲会话基线,但不隔离从 SIGSTOP 到 SIGCONT 的间隔,并且不是运行服务可用性 SLA。
观察重新挂载跨度
29 次保留运行中 145 次成功测量
- p50
- 5毫秒
- p95
- 6毫秒
- 汇集p99
- 7.1 毫秒
- max
- 18毫秒
测量了什么
一个在线空闲会话、一个八层压缩区段、每层 4 KiB,清理宽度为 4。由于各报告不发布 p99,汇总 p99 由原始观测值计算。
用户可能会观察到什么
SIGSTOP pauses application work without closing its sockets. An in-flight request may gain latency or hit its client timeout, but the session is not disconnected. Active-service pause percentiles still need a dedicated SIGSTOP-to-SIGCONT benchmark.
安全上限不是延迟目标
默认冻结握手预算为 500 毫秒,重新挂载runner 超时为 30 秒。 PONR 前不确定性恢复任务; PONR 后的不确定性使它们保持冻结状态,直到会话变为 已销毁,而不是暴露部分切换的挂载。
舞台新的,将旧的移到一边,将新的移到位。
runner 进入 holder 的用户和挂载命名空间。它在挂载移动之前恢复隐藏路径掩码,在路径更改期间保留 O_PATH 描述符,并且从不使用延迟分离。
启动时内核闸门
在运行中的内核通过同 upperdir、新 workdir 的探测(包括 whiteout 与不透明目录行为)之前,在线重新挂载保持禁用。未探测或失败的闸门会把压缩降级为仅提交维护。
- 01
提升面膜
暂时公开 holder挂载命名空间内守护程序拥有的路径。
- 02
准备暂存点
在会话运行目录下创建 暂存、回滚 和新的同级 workdir。
- 03
挂载新款
重复使用会话upperdir,供应压缩下层,并为暂存、回滚和OLD打开O_PATH句柄。
- 04
Restore + probe
在任何移动之前恢复掩码,然后证明暂存根是可读的 OverlayFS挂载。
- 05不归路不归路
移动旧版 → 回滚
第一个 MS_MOVE 分离可见的工作区挂载。它的成功已经到了不归路。
- 06
移动新 →工作区
重新打开底层工作区路径并将暂存覆盖移到公共安装点上。
- 07
探头可见 新
在任何任务恢复之前,通过其保留的描述符验证现在可见的挂载。
- 08
严格卸载 OLD
删除 OLD-root 描述符并卸载 回滚,无需延迟分离。 EBUSY是停放的,不是强制的。
- 09
报告
返回first_move_succeeded、mount_verified和一个类:确定性分类的详细原因。
两个布尔值决定任务是否可以恢复。
父级对 runner 报告进行分类,而不是从进程退出推断成功。如果报告消失,则将holder的工作区挂载ID与切换前的值进行比较。
| runner信号 | 分类 | 租约动作 | Tasks |
|---|---|---|---|
| first_move = false | 清洁跳过 | 发布新·保留旧 | Resume |
| true · verified = false | 故障会话 | 公园新直到销毁 | 保持冷冻状态 |
| true · true · switched | Migrated | 安装新的·释放旧的 | Resume |
| 真·真·回滚 EBUSY | 已核实,已停车 | 安装新的·停放旧的 | Resume |
| 真实 · 真实 · 其他细节 | 故障会话 | 公园新直到销毁 | 保持冷冻状态 |
| 报告失踪 | 比较挂载ID | 相同 = 干净 · 否则有缺陷 | 仅在未更改时恢复 |
manifest更换前
老款manifest仍然具有权威性。进程内失败会删除暂存或升级的 S 目录。
存储后提交
即使每次重新挂载都跳过,压缩仍然持久有效。旧层会继续保持租约,直到会话释放或迁移。
守护进程死亡
仅 RAM 替换和停放租约状态消失。 holder PDEATHSIG和启动收割除死会话; manifest 恢复保持故障关闭状态。
自动压缩是内部调度器,不是另一套存储算法。
架构章节的零参数手册操作仍然存在。当配置的 active-层策略匹配时,当前代码还会从单工作后台引擎运行相同的操作。
产品配置
100 active layers
随附的 prd.yml 启用了 squash_at_n_layers: 100。
架构默认值
disabled when omitted
没有策略的自定义配置不会创建队列和工作线程。
比赛规则
active ≥ threshold
B、L 和之前的 S层计数。配置的最小值为 3。
唤醒信号
startup + real commit
压缩创建的 no-op 发布和 S 层不会递归发出通知。
异步收敛
发布从不等待压缩。
Notify
真正的 L-层提交在审核和会话-销毁 订购后会排队一个便宜的信号。
Coalesce + recheck
有效容量为一的队列合并突发。工作线程读取实时 head,等待共享操作闸门,然后再次读取。
复用压缩流程
相同的计划、展平、提交、重新挂载扫描、遥测和故障语义在后台运行。
阈值并不能保证出块
实时租约边界可以对 manifest 进行分区,因此运行两个原始层是不合格的。工人记录 no_squashable_blocks,保持 head 不变,并等待稍后真正的提交或重新启动 - 没有热重试循环。
恢复持久的真理;收获运行时无法存活的状态。
该守护进程不会尝试复活工作区holder 或重建仅 RAM 的租约。父死亡信号使持久的会话可以证明已死亡,而活动的 manifest 仍然具有存储清理的权限。
- 01
删除导出线轴
删除 RAM 登记表 消失的导出拥有的假脱机目录。
- 02
探测内核层
断言所需的内核行为并设置 live-重新挂载功能闸门。
- 03
收获会话
读取 manager.json,删除包含的运行目录,并重置持久的句柄。
- 04
扫存储
保持活跃manifest层;删除 暂存 和未引用的非基础工件。
什么幸存下来
原子活动 manifest 和每个引用的不可变层保持持久。它们足以在没有实时工作区或租约登记表 的情况下重建当前的层栈视图。
故意丢失什么
实时 命令 登记表、命名空间 holder、内存中租约、替换和停放重新挂载状态不会恢复。他们拥有的会话收获了;客户用耐用的 head 打造出新鲜的会话。
存储删除失败关闭
丢失、不可读、空或预版本的 manifest 不会删除任何内容。通过有效的保持设置,基本层ID 仍然不会被引导扫描删除。
