# 工作流产物、数据血缘与版本切换

从一个数据目录（Catalog）文件或工作流产物查看数据血缘，定位产生结果的算子，并在需要人工修订时创建和切换 Block Revision。数据血缘回答“结果来自哪里”；工作流作业状态回答“任务是否运行成功”，两者不能互相替代。

## 核心对象

| 对象 | 说明 |
| --- | --- |
| 数据目录文件 ID | 从数据目录进入数据血缘的起点。 |
| 工作流产物 ID | 一次 Case 中的产物标识，用于读取产物级拓扑、算子和 Block。 |
| 算子 ID（Node ID） | 产生产物的工作流算子，例如 `root.parse_doc`。 |
| Block ID | 产物中可以独立查看和修订的输出块。 |
| Revision ID | Block 的一个内容版本。 |
| `effective_set_version` | 当前生效 Revision 集合的版本，用于防止并发覆盖。 |

这些 ID 来自接口响应。不要从文件名、算子标签或数组位置自行构造。

## 相关接口

| 方法与路径 | 用途 |
| --- | --- |
| `GET /lineage/catalog-files/{file_id}/overview` | 从数据目录文件查看数据血缘概览。 |
| `GET /lineage/artifacts/{artifact_id}/overview` | 从工作流产物查看完整概览。 |
| `GET /lineage/artifacts/{artifact_id}/nodes/{node_id}` | 查看算子配置和运行快照。 |
| `GET /lineage/artifacts/{artifact_id}/output-blocks` | 列出产物输出 Block。 |
| `GET /lineage/artifacts/{artifact_id}/blocks/{block_id}/revisions` | 列出一个 Block 的 Revision。 |
| `POST /lineage/artifacts/{artifact_id}/blocks/{block_id}/revisions` | 创建 Revision。 |
| `POST /lineage/artifacts/{artifact_id}/effective-revisions` | 切换产物的生效 Revision。 |

算子级接口也提供对应的输出 Block、Revision 和生效版本操作。先从 Overview 取得正确的工作流产物和算子 ID，再选择产物级或算子级路径。

## 1. 查看数据血缘概览

从数据目录文件进入：

```bash
export FILE_ID='<catalog-file-id>'

curl "$PRODUCT_API_BASE_URL/lineage/catalog-files/$FILE_ID/overview" \
  -H "X-API-Key: $PRODUCT_API_KEY" \
  -H "X-Workspace-ID: $WORKSPACE_ID" \
  -H "Accept: application/json"
```

一个文件可能关联多个 Case。需要查看指定 Case 时使用 `case_id` 查询参数。Overview 通常包含：

- `artifact`：当前产物及其来源标识；
- `workflow`：产生该产物的工作流版本；
- `workflow_invocation`：本次工作流调用信息；
- `topology`：完整工作流算子和边；
- `entry`：当前数据目录文件入口。

保存响应中的工作流产物 ID 和生产算子 ID。Overview 返回完整工作流拓扑，不表示每个算子都直接处理了当前文件。

## 2. 查看算子和输出 Block

```bash
export ARTIFACT_ID='<artifact-id-from-overview>'
export NODE_ID='<node-id-from-overview>'

curl "$PRODUCT_API_BASE_URL/lineage/artifacts/$ARTIFACT_ID/nodes/$NODE_ID" \
  -H "X-API-Key: $PRODUCT_API_KEY" \
  -H "X-Workspace-ID: $WORKSPACE_ID" \
  -H "Accept: application/json"
```

算子详情包含算子身份、选中的算子运行以及配置、运行输入、变量、输出、状态和耗时等快照。部分中间算子可能没有持久化的 `runtime_output`；空对象不表示算子没有运行，应结合状态、工作流产物和下游证据判断。

列出产物的输出 Block：

```bash
curl "$PRODUCT_API_BASE_URL/lineage/artifacts/$ARTIFACT_ID/output-blocks" \
  -H "X-API-Key: $PRODUCT_API_KEY" \
  -H "X-Workspace-ID: $WORKSPACE_ID" \
  -H "Accept: application/json"
```

保存要修订的 `block_id`，并记录当前生效 Revision 和 `effective_set_version`：

```bash
export BLOCK_ID='<block-id-from-output-blocks>'
```

## 3. 创建 Revision

创建 Revision 前重新读取 Revision 列表。下面的示例以当前生效内容为基线，并提交新的内容载荷：

```bash
curl -X POST \
  "$PRODUCT_API_BASE_URL/lineage/artifacts/$ARTIFACT_ID/blocks/$BLOCK_ID/revisions" \
  -H "X-API-Key: $PRODUCT_API_KEY" \
  -H "X-Workspace-ID: $WORKSPACE_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "use_current_effective": true,
    "expected_effective_set_version": 7,
    "revision_content_payload": "<REVISION_CONTENT>"
  }'
```

创建接口还支持基于指定 Revision、原始内容、内容引用或 Patch 的方式。一次只选择符合当前任务的基线和内容来源，不要混合互相冲突的字段。保存响应中的新 Revision ID。

## 4. 切换生效 Revision

```bash
curl -X POST \
  "$PRODUCT_API_BASE_URL/lineage/artifacts/$ARTIFACT_ID/effective-revisions" \
  -H "X-API-Key: $PRODUCT_API_KEY" \
  -H "X-Workspace-ID: $WORKSPACE_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "expected_effective_set_version": 7,
    "switches": [
      {
        "block_id": "<BLOCK_ID>",
        "effective_revision_id": "<REVISION_ID>"
      }
    ]
  }'
```

`expected_effective_set_version` 用于并发保护。如果版本冲突，重新读取 Block 和 Revision，确认他人的变更后再提交。不要删除版本检查强行覆盖。

切换成功后保存新的 `effective_set_version`，重新读取输出 Block，并确认下游读取的内容符合预期。切换 Revision 不会自动重新运行依赖算子；需要重新计算时，应使用相应的算子重新运行接口并跟踪新的 Case 或工作流作业 ID。

## 常见问题

| 现象 | 先检查 | 下一步 |
| --- | --- | --- |
| 找不到工作流产物 | 文件 ID、Case 和工作流产物 ID | 从文件 Overview 重新选择 Case，不自行拼接工作流产物 ID。 |
| 算子输出为空 | 算子状态、工作流产物和下游算子 | 不把空 `runtime_output` 直接判定为失败。 |
| Revision 创建冲突 | 基线 Revision、内容哈希和集合版本 | 重新读取最新状态后再创建。 |
| 生效版本切换冲突 | `effective_set_version` | 展示并发变更，让用户重新确认，不覆盖最新结果。 |
| 切换后下游结果未变化 | 是否触发了重新运行 | 根据影响范围重新运行算子，并查询新的工作流作业结果。 |

## 下一步

- [重新运行算子](rerun-nodes.md)
- [运行、查询与取消](run-query-cancel.md)
