# 规模预算验收过程

本页把[规模目标](requirements.zh.md#规模目标)转成可复现的实验。所有时间和内存数字都是验收目标；本页没有百万文件、1 TB 或 100 GB 的执行结果。短回归的已有结果见仓库 `docs/IMPLEMENTATION_AUDIT.md`，不能外推为本页通过。

## 固定输入与容量

- 使用记录了 commit、release 构建命令/feature、配置、CPU 型号/频率、OS/文件系统、物理 RAM、磁盘容量/可用字节、容器/VM 限额的参考设备。先测不经过 CipherFS 的持续读写速度与链路吞吐/RTT，确认符合需求；限制为 3 CPU/6 GiB 的测试容器不等于 8 CPU/16 GiB 的 Full 参考设备。移动级配置必须单独实测，强宿主的运行结果不证明手机预算。
- 固定百万文件夹具：键为 `d/{i // 1000:03}/f{i:06}.bin`，`i=0..999999`；当前正文共 900,000,000,000 bytes，文件 0 为 100,000,000,000 bytes，其余 800,000,000,000 bytes 用 `divmod(800000000000, 999999)` 均分，前 remainder 个文件各多 1 byte。文件 1 有 769 个旧版本，文件 2..99232 各有一个旧版本，每个旧版本 1,000,000 bytes，共 100,000 个未退休旧版本；合计正文正好 1,000,000,000,000 bytes。先写旧版本再写当前版本，mtime 用固定整数毫秒；提交顺序、seed、FileId/VersionId、heads、frontier 和每版本大小/摘要写入清单。固定 10 个成员（2 Full、8 Partial），各 Partial 固定目录正文不超过 8 GiB、`read_cache_max_bytes=8589934592`。初始无退休、无 GC，不用稀疏文件、全零、重复块或硬链接代替数据量。
- 固定 mtime 用 Core 的 `Clock` 注入：`Vault::create_with_clock` 建库，已有库/重开后用 `Vault::set_clock`。成员创建/加入完成后，取这些记录的最大 time_ms 加 1 为基准 B 并保存；第 n 次正文写入前设 `now_ms()=B+n`，在该次 reader 写入直到提交期间保持不变。`prepare_write` 直接用该值记录 mtime，提交顺序按清单递增，不能用 `set_mtime` 再发布一个同正文版本，否则会改变上述未退休版本总量。所有参与节点的夹具时钟不早于已接纳记录，结束前固定于最后值，不让退休/GC 改变输入；wall time 仍用真实单调时钟测量。100 GB 的三个文件也按此方式获得不同且递增的 mtime。
- 内容由可重放的确定性伪随机流生成；每个文件/版本用不同 seed。用标准 SHA-256 counter 流即可：按 UTF-8 编码 `cipherfs-scale-v1:{i}:{version}` 得到 seed，version 为从 0 开始的本文件内容版本序号，依次输出 `SHA256(seed || counter.to_be_bytes(8))`，counter 从 0 递增，最后块截断到声明长度。生成器缓冲不超过 64 KiB，并同时增量计算整文件 SHA-256。清单记录 seed 算法与期望摘要；数据不可压缩性用流式压缩检查另记压缩后字节数，不能用压缩/去重后的大小充当正文大小。
- 实际空间按 `1.2 × (所有设备 CAS + 存放点 pack + 本机索引/日志 + 保留的明文输入/导出 + 最大并发暂存)` 预留，逐项用实测/预计 bytes 相加并向上取整。Full 的 CAS 覆盖全部未退休版本，不仅当前文件；历史、密文 tag、元数据、已上传但待 GC 的对象均计入。百万夹具若 2 Full 和 1 存放点共用磁盘，正文下限为 3 TB，另加 8 台 Partial 的缓存/固定内容等开销后再留 20%。256 GB Partial 只保留完整索引和受预算约束的正文。
- 独立 100 GB 夹具使用下面的流式源，不保留第四份明文：源 CAS、存放点 pack、Full 目标 CAS 三份正文下限 300,000,000,000 bytes，至少准备 360,000,000,000 可用 bytes，并满足上述开销公式。摘要通过 reader/writer 流计算；若另存 100 GB 明文或导出文件，正文下限变成 400,000,000,000 bytes，开销与 20% 余量仍另计。三个根目录不得嵌套、共享 CAS、硬链接或复用目标缓存；Directory 同盘运行另标为存储基线，不当作 1 Gbit/s 网络结果。

## 计时与测量

预生成初始化信息/成员批准和输入清单不计入同步计时，源向存放点发布在 100 GB 实验中计时。密码从安全输入读取，解封成本计入启动；不计人工输密码时间。禁止在计时中生成百万输入或将建库成本混入首次索引同步，但这些准备阶段也须单独记录耗时。每次记录 UTC 起止与单调时钟 wall time；多阶段不能重置总计时以掩盖超限。

| 阶段 | 开始 → 结束 |
| --- | --- |
| 冷启动（30 s） | 本机索引已齐、所有进程退出、重启设备/隔离 VM 清除 OS 页缓存后启动 → 完成解锁并得到首个 limit=256 目录页；离线执行，不取正文。 |
| 页查询（每页 5 s） | 同一静止夹具发起调用 → 得到目录/对象键前缀/历史页及续页值；测根目录和无匹配前缀的页，以及多子项目录、对象键前缀、文件 1 的 770 版本历史的首/中/尾页，另记录扫描 CPU time。百万文件的完整遍历不是一次页调用。 |
| 首次索引（单目标 1,800 s；10 台在线 3,600 s） | 存放点已齐，空目标带已确认的初始化/成员信息启动同步 → 所有文件、旧版本、成员、mtime、heads、精确 frontier 与源清单一致；Full 此后才取正文。对 1 台源和 9 台空目标同时在线另开一轮，测所有目标并发加入并记共享总带宽。 |
| Full 正文（21,600 s） | 索引收敛事件 → 所有未退休正文在目标可读、每版本流式摘要一致、missing_units=0；单目标索引与正文总时长不超过 23,400 s。读取校验包含在此阶段，不能只凭上传 ACK/安全移除状态结束。 |
| 100 GB 全程（14,400 s） | 源 reader 产生第一个 byte → 目标流式读回摘要/大小/mtime/heads/frontier 全部通过；分项计时采用下节的五个边界。 |

每个独立场景做 3 次冷态运行，全部满足预算；暖态重开/查询另做 3 次，分别报告，不能取较快的一次代替冷态。首次同步的冷态必须是新目标目录；源与存放点的 OS 缓存也清冷，方法、采样间隔和是否实际清冷写入证据，做不到时只能标为暖态结果。

每台设备主进程及所有 worker/子进程构成进程树，所有线程与并发正文工作均计入：以不大于 100 ms 间隔采集同时刻 RSS 并求和，辅以 OS 高水位/峰值计数；有子进程退出时保存其终值，报告采样可能漏掉的短峰。10 台并发报告同时刻各树 RSS 之和，上限 5 GiB（5,368,709,120 bytes）。共享页可能重复计入 RSS，不自行扣除。存放点/S3 服务、负载驱动与 OS page cache 分开报告实际峰值，不计入 512 MiB 的客户端树，但整机不得交换到磁盘或 OOM。

live heap 使用覆盖所有线程的分配器计数或等价仪器，记录活跃分配/重分配重叠峰值与工具开销；运行时、数据库缓存、索引/单元引用/选择元数据均包含。RSS 与 live heap 各自须不超过 536,870,912 bytes，二者不能互相替代；未测 heap 时不能宣布内存预算通过。启停阶段、页扫描 CPU、索引/正文后台同时运行时均保留原始采样。

## 100 GB 经存放点的步骤

对 Directory（Range）、S3 loopback（正常 Range 与强制忽略 Range 返回 HTTP 200）、真实 S3（正常 Range；可用时加同等回退代理）分别开干净一轮。loopback 只能证明传输路径；在满足参考链路的网络/整形环境才验收时间预算。真实账号或长期场景未执行时明确写为未验收，不合并为一次通过。

1. **源导入，≤1,800 s。** 在独立源 Full 库用 `put_from_reader`/Core `write_from_reader` 写入 `large.bin`，用上述流的 i=0、version=0 恰好生成 100,000,000,000 bytes，增量 hash 并固定 mtime；计时从第一个生成 byte 到写入提交成功。本阶段尚不连接存放点/对端、不运行发布后台，不能把提前上传从步骤 2 扣掉。源 CAS 必须真实落盘，关闭重开后流式读回检查大小/摘要。该重开检查单独记时，不替代全程最后的目标校验。
2. **存放点发布，≤3,600 s。** 先写 `small/a.bin`、`small/b.bin`，各 65,536 bytes，分别用 i=1、i=2、version=0 的流与递增 mtime、在大文件之后提交，供步骤 4 验证新版本先取；这段准备计入全程。发布分项从源首次发起存放点同步，到所有正文 pack、locator、小 pack 尾包、相关索引/权限依赖及提交头均已到达并可认证；不以首个 head 上传结束。保存对象清单、pack 长度和实际上传顺序，确认小包及依赖先完整可用再供接收方接受。
3. **空 Full 目标索引，≤300 s。** 目标只有初始化信息、批准的身份，无数据库/CAS/正文缓存；关闭源设备/P2P 来源，目标只经存放点同步。计时从同步启动到文件/版本/大小/mtime/heads/精确 frontier 全匹配。索引可见时记录本机已缓存正文和仍缺单元数，验证先有索引后补正文，不能直接复制源数据库。
4. **正文，≤3,600 s。** 从索引收敛到所有未退休正文已持有且 missing_units=0。记录每个批次/版本的进度，验证新版本先取。独立中断轮在至少 1 GB 已持久化后、正文未齐时停止目标（分别覆盖显式取消和进程终止），保存 missing_units（含未尝试的单元），重开同一目标续传；按认证单元哈希去重，已完成单元不重复落盘/计数，不得漏报、重复或发布坏单元。网络重试/整 pack 回退允许重传 wire bytes，须单独统计。恢复活动时间仍受该分项预算约束；预定停机时间单列，并同时报告包含停机的 wall time，全程上限适用于无注入中断的一轮。
5. **目标校验，≤1,800 s。** 关闭重开目标，用 `get_to_writer`/Core `read_to_writer` 把正文送入计数+SHA-256 writer，输出到摘要接收器而非第四个 100 GB 文件；验证完整 100,000,000,000 bytes、SHA-256、mtime、每版本 heads 与 frontier，并检查源/目标所选版本一致。大文件中部/尾部范围另用 `get_range_to_writer` 核对期望流内容；校验出错时丢弃已输出认证前缀，不能当完整文件。记录此阶段所有字节及正文缺失计数。

小版本与必要元数据占用额外容量/时间，全程从步骤 1 到步骤 5 不超过 14,400 s；分项上限之和为 11,100 s，其余余量容纳关闭重开和源校验等操作，不隐去这些耗时。中断轮与无中断轮分别报告；所有无中断冷态运行通过后才称时间预算通过。

每轮报告 `source_plaintext_bytes`、`source_cas_bytes`、`store_ciphertext_bytes`、`wire_bytes`（含 HTTP/QUIC 协议、重试和回退；无法捕获时注明口径）、`target_cas_bytes`、`temporary_peak_bytes`、`missing_units`，另记 AEAD tag、索引/pack locator 与 filesystem allocated bytes。记录源/目标和服务端并发数、fetch 批次配置、页大小及每阶段峰值，不用逻辑文件长度代替实际磁盘或传输量。记录认证缺失与未到达重试，最终不得留有缺失单元。

## 窗口边界与判定

对照实际源码记录下列不同限制；改变配置必须重跑对应预算，不把它们相加当作整进程峰值证明：

| 边界 | 当前默认/格式限制（不是已测进程预算） |
| --- | --- |
| Full fetch 批次 | `ContentFetchOptions::batch_bytes` 为 8 MiB；单元边界允许一单元越过预算，零预算为一单元一批。 |
| 范围读预取 | `vault/range.rs` 的 `PREFETCH_WINDOW_BYTES` 为 32 MiB，按所需单元组窗；与 Full fetch 选项不同。 |
| 认证内容单元 | 明文上限 16 MiB；当前 AEAD tag 为 16 bytes，密文上限 16,777,232 bytes。 |
| 异步流适配器 / 整包暂存 | `synced_vault/streaming.rs` 为两个 64 KiB 通道槽，`object_store.rs` 整包认证复制为 64 KiB 缓冲；整包落盘暂存不代表没有单元/元数据分配。 |
| 远端 pack | 普通 pack 目标 6 MiB、范围 4–8 MiB；超过 4 MiB 的单元独立成包，最后尾包可小于 4 MiB。不能把 8 MiB 普通 pack 上限用于 16 MiB 单元。 |
| 对象与权限 | 存放点单对象上限 32 MiB，权限证明预算 32 MiB，索引段上限 32 MiB；单对象快照/权限证明仍可能阻止百万文件/长历史冷启动，multipart 未实现。 |
| 列表/历史页 | limit=256 用于本实验；API 上限 1024 项，正文引用、heads/争用者、历史遍历 ID、完整索引/成员仍可增长，扫描可能随规模增长。 |

以下任一项即失败：错误摘要/大小/mtime、遗漏或过期 heads/frontier、静默接受部分正文、缺失计数不实、坏单元/错误 pack 发布、未达到容量或参考环境、交换/OOM、任一内存/时间预算溢出。失败保留输出和精确版本，不删除夹具后以短回归宣称规模通过。权限证明/快照触限也按规模未通过记录，不能因正文流有界豁免。

证据至少含精确命令 argv（凭据脱敏）、环境/构建与配置、commit/夹具清单哈希、各阶段起止/退出码、原始 RSS/heap/CPU/网络/磁盘采样、字节口径、重复运行与中断/恢复记录、最终内容/元数据对比和明确通过/失败/未执行状态。当前 512 MiB 本机流与 96 MiB 远端窗口回归分别只证明其用例；它们不证明 100 GB、完整进程 512 MiB 或百万文件目标。本页定义的长期实验属于待办中的规模验收，文档检查不算执行。
