Перейти к содержанию

Происхождение данных на уровне столбцов (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_commands in 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 действительно читает), и конус происхождения сужается до этих двух исходных столбцов. Вывод при этом и корректен, и точен.

См. Команды о механике объявления узкого входного контракта.