# node_id / data_id / 持久化与生命周期说明

本文档说明同步系统中 `node_id` 与 `data_id` 的职责、生成时机、持久化行为，以及同步流程中“创建/绑定/回填”的完整生命周期。目标是避免“重复生成 node_id 导致绑定失效”的问题，并明确各层职责。

---

## 1. 基础概念

- **data_id**
  - 业务主键（来自数据源/远端系统的资源 ID）。
  - 在加载阶段通常已存在；在 CREATE 场景下初始为空，成功后由 DataSource 回填。

- **node_id**
  - 同步系统内部的**稳定标识符**（绑定与持久化的主键）。
  - 用于绑定表、持久化节点、依赖追踪等。
  - 设计目标：**跨同步周期稳定**，不能随每次加载而变化。

---

## 2. 核心结论（必须遵守）

1. **node_id 一旦建立，不应再被数据源“重新生成”。**
   - 绑定关系与持久化状态依赖 `node_id`；若每次加载都重新生成，会导致历史绑定失效。
2. **加载阶段应先恢复 Collection（持久化节点），再加载 DataSource 数据。**
   - 数据源加载出的 `data_id` 应优先匹配已持久化的节点；匹配到则复用旧 `node_id`。
3. **CREATE 场景允许临时/新建 node_id**，但必须通过后续回填与绑定更新保持一致性。

---

## 3. 正确的初始化/加载顺序

### 推荐流程（Pipeline 前半段）

1. **持久化恢复阶段**
  - 必须同时恢复 `node_id` 与 `data_id`（以及状态字段），否则无法建立稳定的映射关系。
  - Collection 可能为空，这属于合法情况。

2. **数据源加载阶段（DataSource.load_all）**
  - 对每一条业务数据（含 `data_id`）：
    - **若 Collection 内已存在该 `data_id` 对应节点**：将数据写回原节点（保留旧 `node_id`）。
    - **若不存在**：由 **Collection** 生成新的 `node_id`，并创建新节点。

> 这样可避免“第二次同步时重建节点，导致旧绑定丢失”。

---

## 4. node_id 的来源与稳定性

### A. 持久化恢复阶段

- **恢复时必须包含 `node_id` 与 `data_id`**：
  - 两者成对恢复，才能保证后续 load 阶段按 `data_id` 命中旧节点。

### B. 数据源加载阶段

- **优先复用持久化 node_id**：
  - 通过 `data_id` 在 Collection 中查找并更新已有节点。
- **仅在未知 data_id 时新建 node_id**：
  - 新节点 `node_id` 必须由 **Collection** 生成，而非 DataSource/Handler 直接拼接。

### C. 创建阶段（Strategy.create）

- 新建“目标端”节点时，**必须有 node_id**：
  - 用于写入 Collection、记录状态、建立临时绑定。
- 成功后回填 `data_id`，**node_id 不变**。

---

## 5. data_id 的来源与回填

本节按阶段拆分说明 `data_id` 的来源与写回。

### A. 持久化恢复阶段

- `data_id` 从持久化数据中恢复（与 `node_id` 一起恢复）。

### B. 数据源加载阶段

- `data_id` 来自数据源原始数据（`id` / `_id`）。
- 若命中已有节点，则仅更新数据与状态，不改变 `node_id`。

### C. 同步创建与轮询阶段

- CREATE 提交后，DataSource 从响应/轮询结果提取 `biz_id`。
- 成功时回填到 `node.data_id`，并回写 `node.data` 的主键字段（仅 `id` / `_id`）。

### D. 持久化阶段

- 将更新后的 `node_id` / `data_id` / 状态字段持久化，供下次同步恢复使用。

---

## 6. 绑定与 node_id 的关系

- 绑定表的 key 为 `node_id`，绑定记录是 `local_node_id <-> remote_node_id`。
- 因此：**node_id 必须稳定**，才能保证绑定长期有效。
- 绑定可在创建前建立“临时关系”，但成功后不应更换 node_id。

---

## 7. 同步周期中的典型场景

### 场景 1：第一次同步（无持久化）
- Collection 为空 → DataSource load 新节点 → 生成新 `node_id`。
- Strategy bind / create / update 执行。
- 持久化后，下次同步可复用 `node_id`。

### 场景 2：第二次同步（已有持久化）
- 先恢复 Collection（已有 `node_id`）。
- DataSource load 时按 `data_id` 匹配旧节点并更新数据。
- 绑定关系仍有效。

### 场景 3：创建新数据（CREATE）
- Strategy 产生目标端节点（`node_id` 新生成）。
- DataSource 执行创建，回填 `data_id`。
- 绑定记录更新为真实 `data_id`（但 node_id 保持不变）。

---

## 8. 关键规则总结

- **node_id 是“同步系统内的稳定 ID”，必须持久化与复用。**
- **data_id 是“业务系统的资源 ID”，由数据源加载或创建后回填。**
- **加载顺序：先恢复持久化节点 → 再 load 数据源。**
- **遇到已存在 data_id：复用 node_id；否则创建新节点。**
- **CREATE 成功后只更新 data_id，不更换 node_id。**

---

## 9. 相关文件参考

- 绑定与状态定义：见 [docs/状态定义规范.md](状态定义规范.md)
- 编排流程：见 [docs/编排流程规范.md](编排流程规范.md)
- BaseDataSource：见 [sync_system_new/datasource/datasource.py](../sync_system_new/datasource/datasource.py)
- BaseApiHandler：见 [sync_system_new/datasource/api/handler.py](../sync_system_new/datasource/api/handler.py)

---

## 10. 明确规则与建议实现

- **DataSource.load_all 必须先按 `data_id` 命中已有节点并更新**，未命中时再由 Collection 创建新节点并分配新的 `node_id`。
- **推荐实现方式**：先 `find_by_data_id`，命中走通用更新；未命中再创建新节点（无需强制 `upsert_by_data_id` 接口）。
- **创建流程中的临时绑定/成功更新规则**：
  - CREATE 节点创建时：`binding_status = NORMAL`，`action = CREATE`，`status = PENDING`，`data_id = ""`。
  - 执行提交前置为 `status = IN_PROGRESS`（异步）或保持 `PENDING`（同步接口待执行）。
  - 成功回填后：`status = SUCCESS`，`data_id` 回填并写回主键字段，绑定记录保持 `node_id` 不变。
  - 过程中出现错误：`status = FAILED`，`error` 记录原因。
- **异常节点持久化/恢复策略**：
  - 持久化时保留异常节点与错误信息。
  - 恢复时是否保留/清理异常节点应通过策略配置控制（默认建议保留以便人工修复）。
