# Capordia — 切换与恢复演练，版本 1.0.0

本 CPU 演练对比连续处理与中断处理：中断处理被复制到另一个文件夹后，
在新进程中恢复。它可以显示哪些批次已完成、进行中和待恢复。它不测量
任何 GPU，也不证明无中断迁移。

## 准备演练

使用 Python 3.10 或更高版本，配合标准模块 `sqlite3` 和 `hashlib`，
在可写入的本地磁盘上运行。不需要任何软件包、网络访问、账户、密钥或
管理员权限。解压文件夹中的所有文件，然后在该文件夹中打开终端。如果
系统使用 `python3` 或 `py -3`，请替换命令中的 `python`。脚本和输入
数据集与其创建的工作文件夹保持分离。

不要在生 产 文件夹或网络共享中运行本演练。选择一个不存在的工作文件夹
名称：程序拒绝复用或覆盖已存在的文件夹。

```text
python exercice.py demonstration --dossier essai-capordia
```

该命令创建 `essai-capordia/rapport-verification.json` 以及每个场景的
文件夹。仅当所有检查通过时，它才以退出码 0 结束。使用新名称重新运行
即可进行新一次尝试。该命令不会删除任何先前的尝试。

## 我们的示例：八批虚构试样

八个 CSV 各包含六条原始且虚构的尺寸测量值。每行包含 `coupon`、
`cible_um` 和 `mesure_um`。单位为整数微米。没有任何数字来自生产机器
或客户。对于每一批，处理会计算试样数量、绝对偏差的总和与最大值，
以及偏差小于或等于 15 µm 的数量。该阈值是演练规则，不是推荐的工业
公差。

结果包含批次编号、配方版本和输入的 SHA-256 指纹。此处使用整数可实现
精确比较。涉及浮点计算的 GPU 负载需要有自己的比较规则和经论证的公差。

## 读取状态

| 注册表中的状态 | 本演练中的含义 | 恢复时的处理 |
|---|---|---|
| `termine` | 结果已接受且状态在同一事务中记录 | 校验；不要重新计算 |
| `en_cours` | 已声明尝试，没有结果被接受 | 检查原授权方，然后允许恢复 |
| `a_reprendre` | 批次尚未开始，或尝试被显式重新排队 | 执行一次尝试 |

在 LOT-004 上注入中断后：LOT-001 至 LOT-003 已完成，LOT-004 进行中，
LOT-005 至 LOT-008 待恢复。LOT-004 没有任何结果被接受。执行
`preparer-reprise` 后，剩下三个已完成、五个待恢复。最终，八个结果必须
与基准的结果相等。只有 LOT-004 有两次尝试。

## 逐步重做切换

以下路径相对于包含 `exercice.py` 的文件夹。中断是在计算之后、接受之前
注入的异常；此阶段的退出码 75 是预期的。进程停止并释放其锁。这既不
是计算机的突然关机，也不是断电。

```text
python exercice.py initialiser --dossier manuel/reference --proprietaire reference
python exercice.py executer --dossier manuel/reference
python exercice.py initialiser --dossier manuel/source --proprietaire atelier-source
python exercice.py executer --dossier manuel/source --interrompre-sur LOT-004
python exercice.py etat --dossier manuel/source
python exercice.py basculer --source manuel/source --destination manuel/cible --proprietaire atelier-cible
python exercice.py preparer-reprise --dossier manuel/cible
python exercice.py executer --dossier manuel/cible
python exercice.py comparer --reference manuel/reference --candidat manuel/cible
```

`basculer` 获取本地锁，校验数据集，关闭源，关闭 SQLite 连接并复制
文件夹。副本在验证结束前保持关闭状态；之后只有目标被允许产出。在已
关闭的源上启动的执行命令会返回退出码 2，并附带 `SOURCE_CLOTUREE`。

比较命令要求每一侧都有八条已接受的结果，检查它们的输入和输出，然后比较结果的规范 JSON 序列化。文本导出可以从 SQLite 重建：它们不是判定某条结果是否被接受的权威依据。

## 生成的文件

| 文件 | 作用 |
|---|---|
| `registre.sqlite` | 参考状态、尝试次数、所有者、关闭状态和已接受的结果 |
| `manifeste.json` 和 `donnees/` | 固定的输入集及预期指纹 |
| `etat.json` | 可读状态和三种状态的计数 |
| `registre-lots.csv` | 导出：id、etat、tentatives、entree_sha256、sortie_sha256 |
| `resultats.json` | 已接受结果的导出，按标识符顺序排列 |
| `rapport-verification.json` | 位于全局演示文件夹中：检查、环境和命令输出 |
| `worker.lock` | 我们程序的临时锁，在正常退出或注入式中断时移除 |

资源中提供的 `registre-lots-vierge.csv` 文件是供你们团队使用的更宽泛模板。它增加了负责人、授权机器、接受标准和恢复原因。它不替代本练习的数据库，也不会被程序导入。

## 已验证的错误

演示还测试了重启已完成的任务、拒绝同一批次的第二次接受、被修改的输入、清单中重复的标识符、被篡改的输出、已存在的锁，以及接受事务内部发生的异常。最后这种情况会留下三条已接受的结果，而不是四条，然后在恢复后重新找到八条结果。回切通过将目标的近期状态复制到第三个文件夹、关闭目标来测试。不会重新激活旧快照。

退出码为：0 成功；2 预期的业务拒绝；75 预期的注入式中断；1 读取或数据库错误。全局场景会明确预期它引发的拒绝。任何其他错误都会导致测试失败。

## 需与结果一并保留的限制

本练习只有一个 worker 参与。锁和关闭字段在由该程序操作的本地文件夹中有效。它们无法阻止某人复制数据库，也无法阻止另一个程序忽略该锁。这不是分布式 fencing。在真正切换之前，旧机器必须通过独立且经过测试的机制失去其发布权利。如果其停止未得到确认，就不要启动第二个权威。

这里的一个事务将结果和状态归入**同一个本地数据库**。它不涵盖向外部系统的发送、通知或已在别处消费的文件。已验证的无重复接受仅限于该数据库和本次练习。SHA-256 在此检测字节与预期指纹之间的差异；一个可随数据一起被修改的清单并不验证其来源的真实性。

如果进程被强制终止，锁可能仍然存在。在进行任何手动干预之前，请核实其确实已停止；本练习绝不会自动删除被判定为陈旧的锁。复制中断会使源保持关闭状态：保留文件夹并调查该事件，不要让两个副本同时处于活动状态。断电、网络文件系统、机器之间的并发、GPU 驱动、框架检查点和性能均未在此进行鉴定。

## 我们的检查结果

2026 年 9 月 24 日，在 Windows AMD64、Python 3.14.6 和 SQLite 3.50.4 下：16 项检查通过，执行 37 条命令，切换后 8 条已接受结果一致。可查阅的证据是 `preuve-execution-cpu.json`。规范结果的指纹是
`bde1bc833092cc8ca481cda5f44cfef36f14f0a94dff3f856624e169f5456660`。
这些数字描述的是这个 48 行的数据集，并非 GPU 基准测试。

有关责任和回切，请参见 [PROCEDURE.md](PROCEDURE.md)；有关一手来源，请参见 [SOURCES.md](SOURCES.md)；有关复用条件，请参见 [LICENSE.txt](LICENSE.txt)。
