# Kestra 双轨迁移与回切运行手册

## 适用范围

本手册用于把单个 DataFlow 从 n8n 正式执行逐步迁移到 Kestra。迁移是逐流程、
逐环境进行的，不允许一次性改变全局正式引擎。

四种受控状态如下：

1. `n8n_primary`：n8n 是唯一正式引擎。
2. `n8n_primary_kestra_shadow`：n8n 正式执行，Kestra 只读或写入隔离目标。
3. `kestra_primary_n8n_standby`：Kestra 正式执行，n8n 保留为可回切目标。
4. `kestra_primary`：仅在统一退役门禁独立批准后允许进入。

状态只能沿相邻路径前进或回退，禁止从 `n8n_primary` 直接跳到
`kestra_primary`。

## 迁移前准备

- 使用 `scripts/inventory_n8n_workflows.py` 取得目标环境的实时资产清单。空的本地
  清单不能替代生产清单。
- 导出一个目标 Workflow，并建立 n8n 节点到 DataOps 数据源 UID 的显式映射。
  映射中不得包含密码、连接串或 API Key。
- 使用 `app.commands.migrate_n8n_workflow` 生成 WorkflowSpec、SchedulePlan 和
  阻断报告。命令只转换，不部署。
- 未支持的社区节点、脚本节点和不能证明幂等性的写节点必须人工改造；转换器不会
  生成任意脚本作为降级方案。
- 为写流程声明影子 Schema、表或对象键前缀。只读流程使用 `read_only` 隔离策略。

## 影子运行与对账

同一次双跑必须使用不可变的同一输入快照，并记录 SHA-256 输入摘要。n8n 仍是
唯一正式写入引擎，Kestra 只能：

- 只读执行；或
- 写入预先声明的隔离目标。

每次执行用 `app.commands.reconcile_dual_run` 对账以下指标：

- 状态、行数、主键摘要、内容摘要和异常数；
- 预先声明的聚合值；
- 执行耗时和资源消耗。

对账文件只包含有界指标和摘要，不保存业务原始行。状态、行数、主键摘要、内容
摘要和异常数不能配置为忽略项。非确定性时间戳或资源波动只能通过显式路径和数值
容差处理。

## 批次顺序和门禁

迁移顺序固定为：

1. 只读、低频、无下游依赖；
2. 幂等分区写；
3. 多节点核心流程；
4. 高风险写、长任务和特殊节点。

进入一个批次前，资产清单、WorkflowSpec、策略、回滚目标、规定次数的影子运行、
一次失败恢复、对账、连接预算、峰值并发和 SLA 必须全部通过。任一门禁缺失即
失败关闭。

## 切换

1. 调用切换服务创建带幂等键的切换请求。
2. 数据库在同一事务中写入 `workflow_cutover_operations`、把迁移状态标记为
   `transition_pending`，并写入 `workflow.engine.cutover.requested` Outbox 事件。
3. 消费者先停用目标 n8n Workflow，再启用 Kestra Flow。
4. 成功后把流程状态提交为 `kestra_primary_n8n_standby`。
5. Kestra 激活失败时立即重新激活 n8n，并把操作记录为 `rolled_back`。原始异常
   和引擎响应不会进入审计，只记录安全原因码与有界状态。

不得在数据库事务中发起 n8n 或 Kestra HTTP 请求。

## 观察期

观察期必须达到流程预先声明的时长，并且满足：

- 没有未解释差异、重复写入或关键 SLA 违约；
- 自动暂停、有限重试和回切至少演练一次；
- n8n 保持 Standby，且没有未授权外部修改；
- 没有实际回切请求。

观察期未结束或出现任一事件时，不允许推进下一批或退役 n8n。

## 显式回切

1. 确认当前状态是 `kestra_primary_n8n_standby`。
2. 使用新的幂等键提交回切。
3. 系统先停用 Kestra，再重新激活 n8n。
4. 数据库状态返回 `n8n_primary_kestra_shadow`。
5. 检查 n8n 已激活、Kestra 已禁用、Outbox 无重复事件，并重新执行针对性对账。

若引擎状态不明确，暂停该流程，不允许同时启用两个正式写入引擎。
