# Packed Backup CSV 导出操作原则

当前已知问题及相关设计记录见：

- [`Workflow 临时 ConfigMap 不会自动回收`](KNOWN_ISSUES/workflow-temporary-configmaps-are-not-garbage-collected.md)；
- [`E2E 回归不能复用 output prefix`](KNOWN_ISSUES/e2e-regression-cannot-reuse-output-prefix.md)；
- [`Controller 通过子进程生成阶段资源`](KNOWN_ISSUES/controller-runs-stage-generators-as-subprocesses.md)；
- [`Controller 通过 kubectl 访问 Kubernetes`](KNOWN_ISSUES/controller-uses-kubectl-for-kubernetes-operations.md)。

本文记录需要操作者判断和维护的流程约束。CronJob 配置字段索引见
[`example/full-example.yaml`](example/full-example.yaml)，测试方法与 E2E 回归说明见
[`TESTING.md`](TESTING.md)。

## 1. Workflow 身份

- 每次新导出使用一个短且唯一的 `RUN_ID`。手工调用建议控制在 24 个字符以内。
- 同一逻辑 workflow 的 reconcile 或 retry 复用原 `RUN_ID`。
- 需要重新选择 backup 或 cluster 时使用新的 `RUN_ID`。
- `export.outPrefix` 可以由多个 workflow 共享。优先让这些 workflow 的 cluster 集合
  互不重叠；可能选中相同 cluster 时采用串行执行或显式协调。

`RUN_ID` 决定 workflow checkpoint、阶段资源和 Export instance 的身份。保存运行记录时，
应同时记录 `RUN_ID`、调用参数、workflow spec revision 和所用 image digest。

## 2. 固化输入与配置变化

首次 cluster selection 会固化 backup 和原始 cluster 集合。恢复期间把 workflow
ConfigMap 与对应的 cluster-list S3 object 视为一组 checkpoint，并保持两者内容一致。

同一 `RUN_ID` 下调整配置前，逐项比较：

- workflow spec 与调用参数；
- Pack shard 数及已有 Job 集合；
- Export output prefix、GPG key、image 和 worker 参数；
- Watermark image、数据库来源和 worker 参数；
- 已完成阶段与准备重建阶段之间的配置边界。

已有 Job 会继续使用创建时的配置，新配置作用于随后创建或明确重建的资源。跨阶段混用
配置可能产生以下风险：

- shard 数与已有 Job 名集合冲突；
- 已完成输出和后续 marker 使用不同的 output prefix 或 GPG key；
- 已完成阶段与后续阶段运行不同 revision；
- 跨日恢复在 checkpoint 尚未形成时选择到不同 backup。

阶段安全变更需要结合已有 Job、checkpoint、S3 pointer 和数据库 watermark 逐项评估。
常规 retry 建议继续使用原 image digest、output prefix、GPG key、shard 数和阶段参数。

## 3. 凭据与输入一致性

- Controller 用于选择 cluster 的 MySQL option file 与 Watermark 挂载的 Secret 应指向
  同一数据库。
- MySQL option file、webhook 和含 Secret data 的 manifest 按凭据管理。持久化产物保存在
  仓库外并限制为当前操作者可读；通过 stdout 传递时使用单一、明确的管道消费者。让输出
  进入日志、命令替换或多路消费者会扩大凭据暴露范围。
- Export 使用经过确认的 OpenPGP 公钥。workflow 生命周期内保持同一把公钥。
- 开始 workflow 后固定关键 image 的不可变 digest，并在 retry 时复用。

## 4. Backup readiness 与并发协调

开始导出前确认选中的 backup、UTC cutoff 和 cluster selection 符合本次任务。对于刚生成
的 backup，先确认 WAL 和相关 chunk 已稳定；必要时按 backup 服务的运维流程执行 flush。

共享 output prefix 时，同一 meta identity 的 writer 会竞争共享 complete marker。发生
条件更新冲突后，按以下信息确认最终归属：

- marker body、metadata 和 ETag；
- marker 当前指向的版本目录；
- 各候选版本对应的 backup time 与 Export instance；
- 相关 workflow 的阶段状态和数据库 watermark。

冲突期间保留候选版本，完成上述盘点后再做精确清理。相同 backup、Export instance 和
meta identity 的 writer 应保持唯一；Pack shard 和人工创建的 Export Job 也应维持
meta identity 不重叠。

大规模 selection 前，根据 cluster 数、Pack shards、Export process 并发、Dumpling
threads、Watermark 并发、存储和数据库连接容量进行人工评估。Pack shard 的选择结果为空
时 cse-ctl 会把该 shard 判为失败，因此增加 shard 数前还要确认所选 keyspace 在分片函数下
能覆盖每个 shard。

跨 backup 的端到端回归应固定两个递增 cutoff，并从两份 metadata 中 `ENABLED` cluster 的
交集构造 fixture。第一轮成功后保留数据库 watermark，只把 `updated_at` 推进到两个 cutoff
之间，可以模拟真实的数据变化并让第二份 backup 自然重新选择同一批 cluster。marker 验收
分别记录 cluster 数和 meta identity 数，因为一个 cluster 可以产生多个 identity，且 identity
集合可能随 backup 变化。主回归不应挑选特定数据来强制触发 Dumpling timeout，但自然发生
的 timeout 仍纳入逐轮输入账目；需要稳定复现 timeout 的故障注入采用独立场景，避免数据
规模和执行速度改变主流程覆盖范围。
初始化工具还必须采用与 Pack 相同的 keyspace eligibility：protobuf 中省略 state 表示默认
`ENABLED`，显式的 `DISABLED`、`ARCHIVED` 或 `TOMBSTONE` keyspace 不能仅因仍保留
`serverless_cluster_id` 配置就写入 `active_cluster_ids`。否则 Controller 的数据库 selection
会大于 Pack 输出，相关行也无法由该 backup 推进 watermark。

## 5. 验收

完整成功至少满足：

- workflow `status=succeeded` 且 `stage=complete`；
- Watermark worker 成功；
- `unverified_complete_marker_clusters=0`；
- `mysql_update_failed_clusters=0`；
- `mysql_updated_clusters`、`packed_clusters` 与预期 cluster 数一致。

以下结果需要人工解释：

- `unverified_complete_marker_clusters` 非零表示降级成功；相关 cluster 保持原 watermark
  并写入 `cluster_blocklist`，应结合 Watermark 和 Export 日志完成原因处置及 blocklist
  管理。
- `newer_complete_marker_clusters` 非零表示共享 marker 已指向更新 backup。
- `newer_mysql_watermark_clusters` 非零表示数据库已有更新 watermark。

验收记录应包含最终 JSON、各阶段 Job 状态、关键日志、marker snapshot、预期 cluster 数和
实际更新数。

## 6. 恢复与 reconcile

恢复前先保存失败现场、Job 描述、events、全部 container 日志和最终 JSON。随后修复根因，
并以原 `RUN_ID` 进行 reconcile。

Job TTL 从 `Complete` 或 `Failed` 后开始计时。TTL 短于单次 workflow 的阶段交接窗口时，
清理可能早于 Controller 汇总 Pack annotation 或读取 Watermark 日志，使当前执行失败；
短于故障发现、取证和恢复窗口时，到期清理会同时移除 Job 及其 Pod 日志，同一 `RUN_ID`
的 reconcile 会把相应阶段视为缺失并重新创建 Job。TTL 应覆盖完整的执行、验收与恢复窗口。

恢复期间保留以下 checkpoint：

- workflow identity ConfigMap 与 cluster-list S3 object；
- Export 和 Watermark 的阶段输入 ConfigMap；
- 已完成 Job；
- S3 complete marker 及其指向的版本目录。

### 6.1 Controller 中断

原 `RUN_ID` 的 reconcile 会继续等待运行中的 Job，并补建缺失阶段。使用与原运行一致的
参数和 spec，避免在恢复过程中引入额外变量。

### 6.2 阶段 Job 失败

定位根因后，仅重建需要重新执行的阶段 Job。保留已完成阶段及其 checkpoint，让后续阶段
继续复用经过验证的输入。

Export 报告 complete marker 条件更新冲突时，先盘点共享 marker、候选版本和并发
workflow，再决定 retry 与清理边界。

Dumpling timeout 可能让 Export、Watermark 和 Controller 以降级状态完成，同时保留
partial candidate 并将相关 cluster 写入 `cluster_blocklist`。常规处理是修复根因、完成
blocklist 处置后由新的 workflow 重新选择该 cluster。需要复用原 `RUN_ID` 时，精确重建
包含该 meta 的 Export Job 和对应 Watermark Job。

版本路径和旧式直系输出的清理验收以本轮已成功写入 complete marker 的 identity 为范围。
将 timeout identity 纳入该断言会把共享 prefix 中早于本轮的历史对象误判为本轮路径格式
问题，因为其执行未到达 marker 更新后的清理阶段。

允许降级完成的回归需要逐轮闭合输入账目：已推进 watermark 的 cluster 与因 marker 未验证、
更新 backup marker 或更新 MySQL watermark 而跳过的 cluster 之和等于该轮输入。marker
未验证的 cluster 还应出现在 `cluster_blocklist`；后续 selection 的预期集合按 watermark
和 blocklist 共同计算。

Watermark 的数据库提交结果存在不确定性时，可以重建该阶段并 reconcile。条件更新和当前
watermark 检查会确认已经提交的结果，并保护更晚的 watermark。

### 6.3 重新选择输入

重新选择 backup 或 cluster 使用新的 `RUN_ID`。新 workflow 可以继续使用共享 output
prefix；执行前确认目标 cluster 的共享 marker 最终应指向哪个 backup 和 Export instance。

## 7. 保留与清理

验收和问题追踪完成前，保留 workflow checkpoint、阶段 Job、Pod 日志、complete marker
和候选输出。

清理前按 `RUN_ID` 建立精确资源清单，并结合 workflow label、stage、job name 和
Kubernetes controller UID 确认资源归属。分别规划以下范围：

- Kubernetes Job、Pod、ConfigMap 和临时存储；
- cluster-list S3 object；
- Export 版本目录与 complete marker；
- 数据库 watermark 与 `cluster_blocklist` 的后续处置。

自动化回归应在创建资源前确认本次 label selector 对应的范围为空，并在创建意图确定后立刻
登记 cleanup target。回归产生的 Job、Pod 和 generic ephemeral PVC 由测试配置的短 TTL
回收；结束时的主动清理范围为已终止 workflow 的临时 Secret 和 ConfigMap。删除前逐项
校验允许的 kind、精确 label、名称和 resource role。尚未终止的 workflow 保留阶段输入，
使 Controller 可以继续运行；校验或访问失败时保留资源并返回失败。扩大 selector 或退化
为模糊名称匹配会把不属于本次执行的资源纳入删除范围。

回归脚本不应内置可能落后于源码的 Controller image。运行者显式提供由待测 revision 构建的
image，并让 Controller 和 Watermark 使用同一份不可变构建；否则 worker 行为与本地断言
可能来自不同 revision，测试结果不能证明当前改动。

complete pointer 成功更新后，严格早于新 backup 的版本会进入自动清理范围；同一或更新
backup 的未引用候选版本需要结合 marker 与 workflow 状态人工盘点。需要长期保留旧
backup 输出时，使用独立 output prefix 或单独归档策略。
