历史复制状态检查
升级到包含九月复制修复的版本可以防止新错误, 但不会改写旧 Content-Encoding、重建丢失的标签,也不能证明历史删除标记清除任务已经收敛。 #201 跟踪存量检查与恢复准备;此次评审尚未确认任何受影响的生产部署。
逐版本只读清单
优先检查 #194 涉及的、被存入 Content-Encoding 的传输 token aws-chunked。
权威站点和副本站点都应检查所有版本。只检查当前对象会漏掉历史版本,普通 COPY 也可能保留来源的错误元数据。
下载并检查只读清单脚本。它使用 Python 3 与 boto3,
只调用 ListObjectVersions 和精确版本的 HeadObject,输出 JSON Lines。
它不读取正文、不写对象、不编辑存储文件,也不收集凭据。
使用安全配置的 AWS profile,为目标范围授予 s3:ListBucketVersions 与 s3:GetObjectVersion 只读权限;
该 profile 与 mcli 别名独立。需要检查 Object Lock 的部署还应配置相应只读权限。
下载脚本及其SHA-256 文件,先验证再执行。此版本摘要为 ccc9d035809b2b41157b4a3f1d35a21108ae4b3af2836e99416a1d2eec1efef2。
随清单保存脚本修订或校验值、客户端依赖版本、Server 身份、桶配置、所选前缀和起止时间。 空前缀覆盖整个桶;对每个相关桶和站点分别执行。每个数据版本需要一次 HEAD,应先从小范围前缀开始,根据部署负载安排扫描。 列表不是原子快照,任何后续修复前应暂停相关变更,或比较重复清单。
最后一条 summary 必须包含 listing_complete: true。退出码 0 表示扫描完成且没有待核查记录,
不表示没有发现错误头部;2 表示存在待核查记录,1 表示列表失败。
中断或其他异常导致没有完整 summary 时,该清单均不完整。
| 分类 | 含义与后续动作 |
|---|---|
confirmed-header |
HEAD 返回格式正常且包含精确 aws-chunked token 的编码列表。建议值仅移除此 token,仍须核验原始字节和受支持的修复操作。 |
ambiguous |
HEAD 失败、LIST/HEAD 身份或状态变化,或编码 token 格式异常、重复、拼写不规范。人工核查,不能把 HEAD 失败当成空头部成功。 |
unaffected-header |
本次成功的精确版本 HEAD 不含该传输 token。不代表历史标签、清除任务、正文完整性或其他版本正常。 |
delete-marker |
列表中的删除标记身份,留待独立清除分析;没有需要规范化的正文。 |
检查按 token 匹配:gzip, aws-chunked 是候选,my-aws-chunked 不是该传输 token。
混合编码保留其他 token 的顺序,大小写变体及重复 token 留给人工核查。
缺少密钥的 SSE-C 版本可能 HEAD 失败,并保持待核查;本工具不接受 SSE-C 密钥。
这些记录应使用批准的、支持密钥的只读流程检查,密钥不能写入报告或命令历史。
清单与私密证据
脚本保存精确 bucket/key/version、列表时间/ETag/大小、原始及建议 Content-Encoding、元数据指纹,以及可读的复制、加密和 Object Lock 字段。 不导出用户元数据值和 KMS 密钥标识。对象名和版本 ID 仍可能敏感,完整清单私下保管,共享报告使用稳定的替代标识。
这是格式示例,并非已发现的生产对象。批准任何写入前,补齐私密记录中的可信来源和版本关系、完整普通/用户元数据、 精确版本标签、保留期/法律保留、SSE 模式和密钥可用性、独立原始字节校验值及复制状态。 缺失字段应视为未知,直到获得相应的授权读取结果。指纹可以检测差异,但不能用于恢复被省略的元数据。
读取原始字节时禁用客户端 Content-Encoding 自动解压,与可信来源版本或独立的已知校验值比较。 真正的 gzip 字节应原样保留,不重新压缩。ETag 不是通用内容校验值,尤其对于分片或加密对象。 来源缺失、受污染或无法信任时,该对象继续保持未解决。
选择并演练修复
- 先确定权威精确版本,再确认副本。多向复制须比较所有参与来源。 仍受污染的来源可能使后续 heal/resync 再次选择元数据复制;这不等于已经证明存在持续重试热循环。
- 形成逐版本的前后变更清单,只移除已确认的传输 token,保留原始字节、实际编码、用户元数据、标签、Object Lock 和加密要求。
- 在版本控制、Object Lock、SSE 和复制配置相同的隔离克隆中演练所选受支持操作。
普通自 COPY 可能创建新版本、改变修改时间和复制排序,不能当作通用的原地元数据修复 API。
必须创建替代版本时,明确版本身份变化和调用方影响。没有受支持的安全操作时,保持未解决,不编辑
xl.meta或内部磁盘文件。 逐一核实目标站点的加密配置和密钥可用性;来源已加密,不能单独证明副本也以加密方式存储。 每次重启后,在所有服务进程及复制目标上使用指定的写入/读取探针,再执行 COPY 或回退。 健康检查和读取成功时,写入法定人数仍可能尚未就绪。 - 真正写入前,在选定的写入协调流程中重新核对精确版本、ETag、大小、元数据指纹、时间戳、标签和锁状态。 先读后写本身不能消除竞争,元数据改变也可能保持 ETag 不变。冲突项跳过并重新检查。
- 在克隆中核对精确版本 HEAD、原始字节校验值不变、所有保留元数据/锁及副本最终收敛, 并演练重启、旧事件延迟到达和具体回滚操作。本地 COPY 返回成功并不足够。 除当前对象读取外,还要检查每个站点的精确版本列表和来源复制状态。 回退后,所有预期副本都应不再列出替代版本;当前对象正确时,其他站点仍可能有待清除版本。
保留不可变清单、完整私密元数据备份及所选操作经过测试的回滚步骤。 如果操作产生了新版本,回滚必须考虑该版本及当前版本关系;再 COPY 一次不能证明回滚。 回退二进制可能重新打开原来的错误入口,也不会自动撤销先前的元数据写入。 写入失败或结果不确定时,先停止并重新检查精确版本,再决定是否重试。 自动重试 COPY 可能再创建一个版本,重复请求不能代替确认前一次操作的实际结果。
标签与删除标记清除
- 标签丢失或复活: 比较各站点精确版本的标签及可用的修订、审计证据。空标签可能是有意删除,不能根据缺失重建历史。 计划新标签操作前需要权威清单;新的标签操作本身会推进修订。
- 删除标记清除: 将预期版本、桶、键、修改时间与 purge/MRF 状态、复制错误一并记录。 保留的标记可能符合预期,或正在等待出站复制;仅凭 405 响应不能证明找到了预期标记,也不能证明已清除。
- 历史 IAM 撤销: 使用独立的 IAM 恢复流程。
验证范围
2026-09-16 使用只读账户,对真实 Server 20260903 存储、停机快照升级到构建 70c7ec4a9fbf(运行时源码基线 40220bd836cb)的克隆,以及重启后的克隆执行检查。
三次清单均为 21 条版本或标记记录:6 条确认存在传输编码、2 条编码待核查、12 条头部不受影响、1 条删除标记。
夹具覆盖非当前版本、null version、特殊对象键、gzip 原始字节,以及带保留期和法律保留的 SSE-S3 对象。
升级保留了原始字节、标签和已核实的锁状态;旧编码头也如预期保留,未被自动修复。
另一次克隆演练通过明确替换元数据的 COPY 修正了一个未锁定当前对象的编码,保留原始 gzip 字节和标签, 但产生了新版本,原版本的错误头部仍然存在。仅删除该新建、未锁定版本后,原当前版本恢复。 该结果证明版本及回滚的区别,并非通用原地修复。最初的环境为 Linux ARM64 单进程、单盘与静态测试 KMS 密钥。
后续演练使用三个站点,每站点两个 Server 进程、四块盘。将 Server 20260903 的停机备份恢复到上述候选构建,
明确配置各站点的 SSE-S3 桶默认加密,使用静态实验 KMS 密钥。
一个站点离线期间,为两个未锁定 gzip 对象创建纠正后的替代版本;该站点返回后,
六个进程的新版 ID、原始 gzip 字节、元数据和标签一致,来源报告复制状态 COMPLETED。
随后对历史版本的标签更新没有改变替代版本的标签。
未参与改写的对照对象始终保留 SSE-S3、GOVERNANCE 保留期和法律保留。
| 阶段 | 每个站点观察到的精确版本清单 |
|---|---|
| 旧版存储与升级后的克隆 | 三个原版本的头部均受影响。 |
| 创建替代版本、离线站点追赶及冷重启 | 两个已纠正的当前版本,加上三个原有受影响版本。 |
| 仅删除两个新建未锁版本,再冷重启 | 三个原版本保留,两个替代版本 ID 均不再出现。 |
通过的演练在 COPY 和回退之前,均对每个进程执行了实际签名写入/读取探针。
早期失败记录仍保留:健康检查和读取成功时,写入曾返回 SlowDownWrite;
在重启后立即发起回退的一次尝试中,180 秒后仍能在副本站点列表中看到替代版本。
该观察不能证明复制永久失败,那次尝试的更长恢复路径未测试。
通过的流程要求实际写就绪,并逐版本确认收敛。
多站点实验运行于同一个隔离 Linux ARM64 容器,共享时钟。 尚未验证 SSE-C、外部 KMS、改写锁定版本、所有延迟事件排列或通用的历史版本原地修复。 详细结果和剩余限制由 #201 跟踪。
清单工具用于准备,不执行修复。具体 Object Lock/SSE/复制配置的验证仍由 #201 跟踪。生产清单扫描与写入应针对选定部署和经评审的变更清单分别记录。
对 confirmed-header 行,proposed_content_encoding: null 表示移除整个 Content-Encoding 字段,不是写入空字符串。其它分类的 null 不代表修复建议。设计背景见副本元数据规范化。