Происхождение данных на уровне столбцов (Column-Level Lineage)¶
Provisa отслеживает происхождение данных на уровне столбцов статически — вычисляемое из определений SQL и контрактов команд, без необходимости выполнения. Доступны два представления: DAG для одного оператора и граф происхождения по всей федерации, охватывающий все зарегистрированные представления и материализованные представления (MV).
Обозреватель происхождения (lineage explorer)¶
Перейдите в Lineage в UI (/lineage). Вставьте оператор SQL и нажмите Build statement
graph, чтобы увидеть его DAG на уровне столбцов. Нажмите Federation graph, чтобы загрузить граф происхождения по
каждому MV в реестре. [tool-verified: LineagePage.tsx:28-119]
DAG уровня оператора (REQ-1160)¶
Каждый именованный выходной столбец в вашем SQL становится узлом. Построитель отслеживает его назад через каждый CTE, подзапрос, соединение и встроенный вызов команды до его исходных столбцов, строя направленный граф от исходных входов до конечных выходов.
Разобранный пример¶
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— узел derived, произведённый SQL-функциейUPPER.
Граница команды не непрозрачна. Поскольку enrich_grpc_set объявляет свои входные столбцы
(id, region) и выходные столбцы (id, embedding, geo), движок происхождения непрерывно замыкает
заражение от объявленных столбцов исходного отношения до каждого выхода.
[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).
- Красное кольцо — участник цикла, классифицированного как ошибка.
- Жёлтое кольцо — участник цикла, классифицированного как обратная связь (feedback loop).
[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]
Ребро от вызова команды отображается в UI как пунктирная фиолетовая линия. [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¶
Возвращает DAG на уровне столбцов для одного оператора SQL.
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"]
}
Возвращает HTTP 422, если SQL не может быть разобран. [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"
}
]
}
Использование происхождения для управления контрактами команд¶
Поскольку замыкание заражения соединяет каждый объявленный входной столбец с каждым объявленным выходным столбцом, широта этого замыкания полностью зависит от того, что вы объявляете.
Рассмотрим команду, которая принимает полную таблицу orders (id, region, amount, customer_id,
discount, notes, ...) и возвращает embedding. Если входной контракт перечисляет все эти
столбцы, каждый последующий столбец, использующий embedding, будет показывать происхождение от всех них.
Это точно, но бесполезно — трудно понять, что на самом деле имело значение.
Объявите только id и text (столбцы, которые модель embedding действительно читает), и конус
происхождения сужается до этих двух исходных столбцов. Вывод при этом и корректен, и точен.
См. Команды о механике объявления узкого входного контракта.