Files
ecm_sync_system/docs/archive/docs/持久化与人工介入规范.md
T
strepsiades 4a9c444b10 first commit
2026-03-09 16:31:42 +08:00

153 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 节点状态持久化与人工介入规范
> **代码实现**`sync_system_new/common/state_reset.py`
---
## 1. 持久化规则
### 1.1 持久化目标
| 目标 | 说明 |
|------|------|
| **可恢复性** | 系统重启后能从上次状态继续 |
| **可观测性** | 失败和异常状态要保留,供人工查看 |
| **可追溯性** | 失败信息持久化,不自动重试,需人工确认 |
| **绑定稳定性** | 手工绑定一旦建立,永不自动改变 |
### 1.2 分阶段重置设计
状态重置分为两个阶段执行:
#### 阶段1:加载时重置(Collection.load_from_persistence
| 字段 | 性质 | 持久化 | 阶段1重置 |
|------|------|--------|-----------|
| `node_id` | 恒定标识 | ✅ 必须 | ❌ 不重置 |
| `data_id` | 业务主键 | ✅ 必须 | ❌ 不重置 |
| `data` | 业务数据 | ✅ 必须 | ✅ **重置**(每次重新取数) |
| `origin_data` | 原始快照 | ✅ 必须 | ✅ **重置**(每次重新取数) |
| `depend_ids` | 依赖列表 | ❌ **不持久化** | ✅ **初始化为[]**(bind 阶段从 data 实时计算) |
| `binding_status` | 绑定状态 | ✅ 必须 | ✅ **重置为 UNCHECKED** |
| `action` | 同步意图 | ✅ 保存 | ❌ **保留**(用于阶段2判断) |
| `status` | 执行状态 | ✅ 保存 | ❌ **保留**(全部保留,不重置) |
| `error` | 错误信息 | ✅ 保存 | ✅ **重置为None**(清空上次错误) |
| `context` | 扩展上下文 | ✅ 保存 | ❌ 不重置 |
#### 阶段2:数据加载后清理(Pipeline._cleanup_create_failed_zombies
在数据加载完成后、策略执行前,执行CREATE失败节点清理:
1. **检查条件**
- 本地节点 `action == CREATE`(上次尝试创建)
- 本地节点 `status == FAILED`(创建失败)
2. **清理动作**
- **删除本地失败节点**(从 collection 中移除)
- 如果有绑定记录:
- 解除绑定关系
- 删除远程 collection 中的僵尸节点
3. **最后重置 action**
- 所有剩余节点的 `action` 重置为 NONE
- 准备进入策略阶段
4. **下次同步的自动恢复**
- 被删除的节点会从数据源重新加载
- 如果远程实际已创建成功(网络波动导致未获取到结果)→ 绑定阶段会自动绑定上
- 如果远程实际也失败了 → 重新尝试创建
5. **UPDATE 阶段保护**
- UPDATE 操作会跳过所有 `status == FAILED` 的节点
- 防止 UPDATE 阶段覆盖 CREATE/UPDATE 失败的状态和错误信息
- 确保失败节点的 action 和 error 信息保持不变,直到下次同步清理
> **说明**
> - `data` / `origin_data`:每次运行从数据源重新获取,保证数据最新
> - `depend_ids`**不持久化**,每次 bind 阶段从 data 的业务字段(如 contract_id, project_id)实时提取
> - `binding_status`:持久化用于审计,加载时重置为 `UNCHECKED`,由 bind() 重新判定
> - `error`:**阶段1清空**,避免上次错误影响本次运行,新错误会在各阶段重新生成
> - `action`**阶段1保留**用于识别 CREATE 失败的节点,**阶段2清理后重置**
> - `status`**阶段1全部保留**(包括 FAILED、PRECONDITION_FAILED 等),不自动重置
> - **CREATE 失败节点会被自动删除并重新加载**,UPDATE/DELETE 失败保留需人工处理
> - **UPDATE 操作会跳过 FAILED 节点**,避免覆盖失败状态和错误信息
### 1.3 绑定记录 vs binding_status
| 概念 | 存储位置 | 持久性 | 如何改变 |
|------|---------|--------|---------|
| **绑定记录** | `bindings` 表 | **永久保留** | 只能手工 bind/unbind,或 CREATE 失败自动清理 |
| **binding_status** | `nodes` 表 | 保存但可重判 | 每次 bind() 重新判定 |
### 1.4 CREATE 失败的自动恢复
当 CREATE 操作失败时,会产生"僵尸绑定":
- 绑定记录已建立(local_id → remote_node_id
- 但远程节点的 `data_id` 为空(API 未返回真实 ID
**下次同步时的自动恢复流程**
1. 阶段1加载:保留 `action=CREATE`
2. 阶段2清理:检测到僵尸绑定,自动清理
3. 本地节点恢复为 MISSING 状态
4. 策略阶段重新判定,再次尝试 CREATE
---
## 2. 人工介入规范
### 2.1 介入时机
| 时机 | 场景 | 操作 |
|------|------|------|
| **运行前** | 查看上次失败记录 | 查看持久化的 error,决定是否修复后重试 |
| **bind() 后** | 出现 ABNORMAL/WARNING | 检查数据一致性,手工修复或确认 |
| **bind() 后** | 1:N / N:N 映射 | 手工绑定指定远程记录 |
| **sync() 后** | 批量失败 | 分析失败原因,决定重试策略 |
### 2.2 binding_status 与介入需求
| binding_status | 含义 | 需要介入 | 说明 |
|----------------|------|---------|------|
| `UNCHECKED` | 未检查 | ❌ | 等待 bind() 判定 |
| `NORMAL` | 绑定正常 | ❌ | 自动处理 |
| `MISSING` | 无绑定记录 | ❌ | 自动创建 |
| `WARNING` | 本地有绑定但远程无数据 | ⚠️ 可选 | 可能是远程删除,需确认 |
| `ABNORMAL` | 状态不一致 | ✅ **必须** | 需人工排查 |
| `DEPENDENCY_ERROR` | 依赖未就绪 | ❌ | 等依赖解决后自动重试 |
### 2.3 人工绑定操作
```python
# 手工绑定(1:N 场景)
binding_manager.bind(node_id="local_xxx", data_id="remote_xxx")
# 手工解绑
binding_manager.unbind(node_id="local_xxx")
```
绑定记录一旦建立,永不自动改变,只能手工操作。
### 2.4 职责分工
| 职责 | 系统自动 | 人工介入 |
|------|---------|---------|
| 1:1 自动绑定 | ✅ | - |
| 1:N / N:N 绑定 | ❌ | ✅ |
| NORMAL 节点同步 | ✅ | - |
| MISSING 节点创建 | ✅ | - |
| CREATE 失败重试 | ✅(自动清理僵尸) | ⚠️(需确认后手动重置) |
| UPDATE 失败重试 | ❌(保留FAILED状态) | ✅(需确认后手动重置) |
| ABNORMAL 处理 | ❌ | ✅ |
| WARNING 确认 | ❌ | ✅(可选) |
---
## 3. 总结
1. **分阶段重置**
- 阶段1(加载时):只重置 binding_status**保留 action/status/error**
- 阶段2(数据加载后):清理 CREATE 失败僵尸,然后重置 action
2. **失败状态持久化**FAILED 状态和 error 信息保留,不自动重试
3. **绑定记录**:永久保留,除非 CREATE 失败自动清理或手工操作
4. **人工介入**:失败需确认后手动重置,ABNORMAL/WARNING 需人工处理,1:N 绑定需手工指定