列级数据血缘¶
Provisa以静态方式追踪列级数据血缘——根据SQL定义和命令合约计算得出,无需执行。系统提供两种视图:单条语句的DAG,以及涵盖所有已注册视图和物化视图(MV)的联邦级溯源图。
血缘浏览器¶
在界面中导航至Lineage(/lineage)。粘贴一条SQL语句并点击Build statement graph,即可查看其列级DAG。点击Federation graph,可加载涵盖注册表中每个MV的溯源图。[tool-verified: LineagePage.tsx:28-119]
语句级DAG(REQ-1160)¶
SQL中每个具名输出列都会成为一个节点。构建器会沿着每个CTE、子查询、join及内联命令调用向上回溯,直至其来源列,从而构建出由来源输入到最终输出的有向图。
示例演算¶
SELECT o.id, e.embedding, upper(e.geo) AS geo_u
FROM orders o
JOIN enrich_grpc_set('main.public.orders') e ON o.id = e.id
该语句产生三个输出列。geo_u的图如下:
orders.geo ──[enrich_grpc_set(...)]──► e.geo ──[UPPER]──► geo_u
orders.id ─╮ (taint closure)
orders.region ─╯
orders.id、orders.region和orders.geo是source节点(enrich_grpc_set的窄输入合约声明了id和region;完整的taint closure会将所有已声明的输入连接到所有输出)。[tool-verified:_splice_commandsin graph.py:223-242]e.embedding和e.geo是command节点——即enrich_grpc_set的边界。geo_u是由UPPER这个SQL函数产生的derived节点。
命令边界并非不透明。由于enrich_grpc_set声明了其输入列(id、region)和输出列(id、embedding、geo),血缘引擎会将taint closure从来源关系已声明的列持续连接至每个输出。[tool-verified: _splice_commands and _input_relation in graph.py:245-271]
节点种类与视觉提示¶
[tool-verified: LineageDag.tsx:25-29, KIND_COLOR constants; LineagePage.tsx:21-26 LEGEND]
| 节点种类 | 颜色 | 含义 |
|---|---|---|
source |
绿色 | 基础表的一列 |
derived |
蓝色 | 由SQL表达式(函数、运算符、CTE)产生 |
command |
紫色 | 已注册命令的输出列 |
节点上的附加圆环:
- 橙色圆环——该语句的最终输出列。
- 双边框——该列所属的关系是物化视图(MV/CTAS快照)。
- 红色圆环——被归类为错误的循环成员。
- 黄色圆环——被归类为反馈回路的循环成员。
[tool-verified: LineageDag.tsx:88-103 Cytoscape style selectors]
边上的具名转换¶
每条边都携带产生目标列的原始SQL表达式,以及一份具名操作列表:SQL函数(sql_function)、算术/逻辑运算符(operator)、已注册命令(command)、纯列引用(identity)和字面量(constant)。[tool-verified: TransformOp and name_transform in graph.py:36-145]
来自命令调用的边,会在界面中以紫色虚线表示。[tool-verified: LineageDag.tsx:122-124]
联邦级图(REQ-1161)¶
联邦图将每个已注册MV的语句级血缘合并为单一溯源图。节点的身份为relation.column——某视图的输出列与另一视图对同一列的输入引用会合并为单一节点。结果是从基础来源列延伸至平台上每个衍生数据集的单一DAG。[tool-verified: build_federation_graph in merge.py:205-229 and qualify_outputs in graph.py:275-299]
可使用focus、direction和depth,在不重新计算图的情况下,于联邦规模上限定视图范围。[tool-verified: slice_graph in merge.py:160-189]
循环(REQ-1161)¶
循环会被描述,而非被拒绝。血缘引擎会检测每个有向循环并将其分类。[tool-verified: Cycle.classification property in merge.py:43-46]
| 分类 | 边框颜色 | 含义 |
|---|---|---|
feedback |
黄色 | 该循环经过一个物化节点——属合法、具时间延迟的反馈回路。MV快照即是使其成为良定义的版本边界。 |
error |
红色 | 该回路上没有物化边界——属无稳定求值顺序的循环定义,很可能是设计错误。 |
[tool-verified: LineagePage.tsx:83-98 cycle alert rendering; merge.py:38-48]
feedback循环并非失败。若增强型MV将衍生列反馈至其自身的来源关系,只要回路上有一个节点已物化,这便是有效模式——快照会在时间上将两部分隔开。error循环则需要运维人员判断:通常意味着两个视图相互引用,中间没有快照。
API¶
两个端点均为静态——只读取定义和合约,不读取数据。
POST /admin/lineage/graph¶
返回单条SQL语句的列级DAG。
POST /admin/lineage/graph
Content-Type: application/json
{
"sql": "SELECT o.id, e.embedding FROM orders o JOIN enrich_grpc_set('main.public.orders') e ON o.id = e.id",
"dialect": "postgres"
}
[tool-verified: lineage_graph endpoint at lineage_router.py:45-54, LineageGraphRequest model at
lineage_router.py:29-31]
响应结构[tool-verified: LineageGraph.to_dict in graph.py:82-105]:
{
"nodes": [
{"id": "orders.id", "column": "id", "relation": "orders", "kind": "source", "materialized": false}
],
"edges": [
{
"source": "orders.id",
"target": "e.id",
"transform": "enrich_grpc_set(...)",
"ops": [{"name": "enrich_grpc_set", "kind": "command"}]
}
],
"outputs": ["id", "embedding"]
}
当SQL无法解析时,返回HTTP 422。 [tool-verified: lineage_router.py:51-54]
GET /admin/lineage/federation¶
返回注册表中所有MV的合并溯源图。
GET /admin/lineage/federation
GET /admin/lineage/federation?focus=orders.id&direction=downstream&depth=3
[tool-verified: federation_graph endpoint at lineage_router.py:73-98]
查询参数[tool-verified: function signature at lineage_router.py:73-76]:
| 参数 | 取值 | 默认值 | 效果 |
|---|---|---|---|
focus |
节点ID | — | 将响应限定于该节点周围的子图 |
direction |
upstream | downstream | both |
both |
从focus出发的遍历方向 |
depth |
整数 | 无限制 | 距focus的最大跳数 |
响应结构与语句图相同,并附加一个cycles字段
[tool-verified: MergedGraph.to_dict in merge.py:60-64]:
{
"nodes": [...],
"edges": [...],
"outputs": [...],
"cycles": [
{
"nodes": ["orders.region", "enriched_orders.region"],
"has_materialization_boundary": true,
"classification": "feedback"
}
]
}
利用血缘治理命令合约¶
由于taint closure会将每个已声明的输入列连接到每个已声明的输出列,该closure的广度完全取决于你声明了什么。
试想一个命令,接收完整的orders表(id、region、amount、customer_id、discount、notes等),并返回一个embedding。若输入合约列出全部这些列,则每个使用该embedding的下游列都会显示来自全部这些列的血缘。这虽然准确,却并不实用——难以判断实际上是什么真正起了作用。
只声明id和text(即embedding模型实际读取的列),血缘范围便会收窄至这两个来源列。这样得出的推导既严谨又精确。
有关声明窄输入合约的机制,请参阅Commands。