Skip to content

0.2.0 (2025-03-25)

Кратко

  • Реализована поддержка lineage на уровне колонок.
  • Партиции HDFS/S3 теперь удаляются из пути таблицы.
  • Добавлена общая статистика по запускам/операциям (входные/выходные байты, строки, файлы).
  • Улучшения UX графа lineage
  • Улучшения в интеграции Kafka -> потребитель.

Критические изменения

  • Изменена схема ответа GET /operations. (#158)

Свойства операций перемещены в ключ data, добавлен новый ключ statistics. Это позволяет отображать статистику операций в UI без построения графа lineage

Примеры ответов операций

=== Было

{
    "meta": {
        // ...
    },
    "items": [
        {
            "kind": "OPERATION",
            "id": "00000000-0000-0000-0000-000000000000",
            "name": "abc",
            "description": "some",
            // ...
        }
    ],
}

=== Стало

{
    "meta": {
        // ...
    },
    "items": [
        {
            "id": "00000000-0000-0000-0000-000000000000",
            "data": {
                "id": "00000000-0000-0000-0000-000000000000",
                "name": "abc",
                "description": "some",
                // ...
            },
            "statistics": {
                "inputs": {
                    "total_datasets": 2,
                    "total_bytes": 123456,
                    "total_rows": 100,
                    "total_files": 0,
                },
                "outputs": {
                    "total_datasets": 2,
                    "total_bytes": 123456,
                    "total_rows": 100,
                    "total_files": 0,
                },
            },
        }
    ],
}
  • Изменена схема ответа GET /runs. (#159)

Свойства запуска (Run) перемещены в ключ data, добавлен новый ключ statistics. Это позволяет отображать статистику запуска в UI без построения графа lineage

Примеры ответов запусков (Run)

=== Было:

{
    "meta": {
        // ...
    },
    "items": [
        {
            "kind": "RUN",
            "id": "00000000-0000-0000-0000-000000000000",
            "external_id": "abc",
            "description": "some",
            // ...
        }
    ],
}

=== Стало

{
    "meta": {
        // ...
    },
    "items": [
        {
            "id": "00000000-0000-0000-0000-000000000000",
            "data": {
                "id": "00000000-0000-0000-0000-000000000000",
                "external_id": "abc",
                "description": "some",
                // ...
            },
            "statistics": {
                "inputs": {
                    "total_datasets": 2,
                    "total_bytes": 123456,
                    "total_rows": 100,
                    "total_files": 0,
                },
                "outputs": {
                    "total_datasets": 2,
                    "total_bytes": 123456,
                    "total_rows": 100,
                    "total_files": 0,
                },
                "operations": {
                    "total_operations": 10,
                },
            },
        }
    ],
}
  • Изменена схема ответа GET /locations. (#160)

Свойства location перемещены в ключ data, добавлен новый ключ statistics. Это позволяет отображать статистику location в UI.

Примеры ответов Location

=== Было

{
    "meta": {
        // ...
    },
    "items": [
        {
            "kind": "LOCATION",
            "id": 123,
            "name": "rnd_dwh",
            "type": "hdfs",
            // ...
        }
    ],
}

=== Стало

{
    "meta": {
        // ...
    },
    "items": [
        {
            "id": "123",
            "data": {
                "id": "123",
                "name": "rnd_dwh",
                "type": "hdfs",
                // ...
            },
            "statistics": {
                "datasets": {"total_datasets": 2},
                "jobs": {"total_jobs": 0},
            },
        }
    ],
}

То же самое для PATCH /locations/:id:

Примеры ответа Location

=== Было

{
    "kind": "LOCATION",
    "id": 123,
    "name": "abc",
    // ...
}

=== Стало

{
    "id": "123",
    "data": {
        "id": "123",
        "name": "abc",
        // ...
    },
    "statistics": {
        "datasets": {"total_datasets": 2},
        "jobs": {"total_jobs": 0},
    },
}
  • Изменена схема ответа GET /datasets. (#161)

Свойства набора данных перемещены в ключ data. Это делает API-ответ более согласованным с другими (например, GET /runs, GET /operations).

### Примеры ответов

=== Было

{
    "meta": {
        // ...
    },
    "items": [
        {
            "kind": "DATASET",
            "id": 123,
            "name": "abc",
            // ...
        }
    ],
}

=== Стало

{
    "meta": {
        // ...
    },
    "items": [
        {
            "id": "123",
            "data": {
                "id": "123",
                "name": "abc",
                // ...
            },
        }
    ],
}
  • Изменена схема ответа GET /jobs. (#162)

Свойства задания (Job) перемещены в ключ data. Это делает API-ответ более согласованным с другими (например, GET /runs, GET /operations).

Примеры ответов Jobs

=== Было

{
    "meta": {
        // ...
    },
    "items": [
        {
            "kind": "JOB",
            "id": 123,
            "name": "abc",
            // ...
        }
    ],
}

=== Стало

{
    "meta": {
        // ...
    },
    "items": [
        {
            "id": "123",
            "data": {
                "id": "123",
                "name": "abc",
                // ...
            },
        }
    ],
}
  • Изменена схема ответа GET /:entity/lineage. (#164)

Список всех узлов (например, list[Node]) разделен по типу узла и преобразован в словарь (например, dict[str, Dataset], dict[str, Job]).

Список всех отношений (например, list[Relation]) разделен по типу отношения (например, list[DatasetSymlink], list[Input]).

Примеры ответов lineage

=== Было

{
    "relations": [
        {
            "kind": "PARENT",
            "from": {"kind": "JOB", "id": 123},
            "to": {"kind": "RUN", "id": "00000000-0000-0000-0000-000000000000"},
        },
        {
            "kind": "SYMLINK",
            "from": {"kind": "DATASET", "id": 234},
            "to": {"kind": "DATASET", "id": 999},
        },
        {
            "kind": "INPUT",
            "from": {"kind": "DATASET", "id": 234},
            "to": {"kind": "OPERATION", "id": "11111111-1111-1111-1111-111111111111"},
        },
        {
            "kind": "OUTPUT",
            "from": {"kind": "OPERATION", "id": "11111111-1111-1111-1111-111111111111"},
            "to": {"kind": "DATASET", "id": 234},
        },
    ],
    "nodes": [
        {"kind": "DATASET", "id": 123, "name": "abc"},
        {"kind": "JOB", "id": 234, "name": "cde"},
        {
            "kind": "RUN",
            "id": "00000000-0000-0000-0000-000000000000",
            "external_id": "def",
        },
        {
            "kind": "OPERATION",
            "id": "11111111-1111-1111-1111-111111111111",
            "name": "efg",
        },
    ],
}

=== Стало

{
    "relations": {
        "parents": [
            {
                "from": {"kind": "JOB", "id": "123"},
                "to": {"kind": "RUN", "id": "00000000-0000-0000-0000-000000000000"},
            },
        ],
        "symlinks": [
            {
                "from": {"kind": "DATASET", "id": "234"},
                "to": {"kind": "DATASET", "id": "999"},
            },
        ],
        "inputs": [
            {
                "from": {"kind": "DATASET", "id": "234"},
                "to": {
                    "kind": "OPERATION",
                    "id": "11111111-1111-1111-1111-111111111111",
                },
            },
        ],
        "outputs": [
            {
                "from": {
                    "kind": "OPERATION",
                    "id": "11111111-1111-1111-1111-111111111111",
                },
                "to": {"kind": "DATASET", "id": "234"},
            },
        ],
    },
    "nodes": {
        "datasets": {
            "123": {"id": "123", "name": "abc"},
        },
        "jobs": {
            "234": {"id": "234", "name": "cde"},
        },
        "runs": {
            "00000000-0000-0000-0000-000000000000": {
                "id": "00000000-0000-0000-0000-000000000000",
                "external_id": "def",
            },
        },
        "operations": {
            "11111111-1111-1111-1111-111111111111": {
                "id": "11111111-1111-1111-1111-111111111111",
                "name": "efg",
            },
        },
    },
}

Это позволяет заменить фильтры на стороне UI со сложностью O(n), например:

// O(n)
relations.filter((relation) => relation.kind == "INPUT" && relation.from.kind == "DATASET" && relation.from.id == dataset_id)
// снова O(n)
nodes.filter((node) => node.kind == "DATASET" && node.id == dataset_id)

на гораздо более эффективные:

// O(n) с гораздо меньшим n
relations.inputs.filter((relation) => relation.from.kind == "DATASET" && relation.from.id == dataset_id)
// O(1)
nodes.datasets[dataset_id]

Размер выходного JSON не сильно отличается.

Обратите внимание, что идентификаторы наборов данных (dataset), заданий (Job) и локаций (location) во всех ответах были преобразованы из целых чисел в строки, поскольку в JSON ключи объектов должны быть строками.

Также узлы и отношения больше не имеют поля kind.

  • Изменен тип значения DATA_RENTGEN__KAFKA__BOOTSTRAP_SERVERS со строки (один элемент host:port) на список (["host1:port1", "host2:port2"]). (#183)
  • Синхронизированы значения DATA_RENTGEN__KAFKA__SECURITY__TYPE со значениями клиента Kafka, например, scram-sha256 -> SCRAM-SHA-256. (#183)

Функциональность

  • Потребитель теперь может захватывать и сохранять происхождение колонок OpenLineage. (#155)

Настоятельно рекомендуется обновиться до OpenLineage 1.23 и использовать columnLineage.datasetLineageEnabled=true, чтобы уменьшить как размер JSON события, так и снизить нагрузку на CPU потребителя DataRentgen.

  • Включен суммарный расчет входов и выходов в ответы lineage (#171)

Например, если пользователь запросил происхождение с granularity=OPERATION, включать входы и выходы с детализацией до RUN (сумма всех включенных операций по run_id) и JOB (сумма всех включенных операций по job_id). Это позволяет показать, что определенная операция составляет некоторый процент от всех операций в рамках этого запуска или задания.

  • Добавлено lineage колонок в ответы lineage GET /:entity/lineage. (#172)

### Еще примеры ответов lineage

=== Было

{
    "relations": {
        "parents": [
            {
                "from": {"kind": "JOB", "id": "123"},
                "to": {"kind": "RUN", "id": "00000000-0000-0000-0000-000000000000"},
            },
        ],
        "symlinks": [
            {
                "from": {"kind": "DATASET", "id": "234"},
                "to": {"kind": "DATASET", "id": "999"},
            },
        ],
        "inputs": [
            {
                "from": {"kind": "DATASET", "id": "234"},
                "to": {
                    "kind": "OPERATION",
                    "id": "11111111-1111-1111-1111-111111111111",
                },
            },
        ],
        "outputs": [
            {
                "from": {
                    "kind": "OPERATION",
                    "id": "11111111-1111-1111-1111-111111111111",
                },
                "to": {"kind": "DATASET", "id": "234"},
            },
        ],
    },
    "nodes": {
        "datasets": {
            "123": {"id": "123", "name": "abc"},
        },
        "jobs": {
            "234": {"id": "234", "name": "cde"},
        },
        "runs": {
            "00000000-0000-0000-0000-000000000000": {
                "id": "00000000-0000-0000-0000-000000000000",
                "external_id": "def",
            },
        },
        "operations": {
            "11111111-1111-1111-1111-111111111111": {
                "id": "11111111-1111-1111-1111-111111111111",
                "name": "efg",
            },
        },
    },
}

=== Стало

{
    "relations": {
        "parents": [
            {
                "from": {"kind": "JOB", "id": "123"},
                "to": {"kind": "RUN", "id": "00000000-0000-0000-0000-000000000000"},
            },
        ],
        "symlinks": [
            {
                "from": {"kind": "DATASET", "id": "234"},
                "to": {"kind": "DATASET", "id": "999"},
            },
        ],
        "inputs": [
            {
                "from": {"kind": "DATASET", "id": "234"},
                "to": {
                    "kind": "OPERATION",
                    "id": "11111111-1111-1111-1111-111111111111",
                },
            },
        ],
        "outputs": [
            {
                "from": {
                    "kind": "OPERATION",
                    "id": "11111111-1111-1111-1111-111111111111",
                },
                "to": {"kind": "DATASET", "id": "234"},
            },
        ],
        // Здесь |
        //      v
        "direct_column_lineage": [
            {
                "from": {"kind": "DATASET", "id": "234"},
                "to": {"kind": "DATASET", "id": "235"},
                "fields": {
                    "target_column_1": [
                        {
                            "field": "direct_source_column_1",
                            "last_used_at": "2008-09-15T15:53:00+05:00",
                            "types": [
                                "TRANSFORMATION_MASKING",
                                "AGGREGATION",
                            ],
                        },
                        {
                            "field": "direct_source_column_2",
                            "last_used_at": "2008-09-15T15:53:00+05:00",
                            "types": [
                                "AGGREGATION",
                            ],
                        },
                    ],
                    "target_column_2": [
                        {
                            "field": "direct_source_column_1",
                            "last_used_at": "2008-09-15T15:53:00+05:00",
                            "types": [
                                "TRANSFORMATION_MASKING",
                                "AGGREGATION",
                            ],
                        },
                    ]
                },
            },
        ],
        "indirect_column_lineage": [
            {

                "from": {"kind": "DATASET", "id": "234"},
                "to": {"kind": "DATASET", "id": "235"},
                "fields": [
                    {
                        "field": "indirect_source_column_1",
                        "last_used_at": "2008-09-15T15:53:00+05:00",
                        "types": ["JOIN"],
                    },
                ]
            },
        ],
    },
    "nodes": {
        "datasets": {
            "123": {"id": "123", "name": "abc"},
        },
        "jobs": {
            "234": {"id": "234", "name": "cde"},
        },
        "runs": {
            "00000000-0000-0000-0000-000000000000": {
                "id": "00000000-0000-0000-0000-000000000000",
                "external_id": "def",
            },
        },
        "operations": {
            "11111111-1111-1111-1111-111111111111": {
                "id": "11111111-1111-1111-1111-111111111111",
                "name": "efg",
            },
        },
    },
}
  • Добавлена поддержка аутентификации Kafka GSSAPI. (#183)
  • Разрешено получение GET /v1/runs?since=... без параметра запроса search_query. (#184)

Улучшения

  • Исправлен множественный proxyUrl для спарк-фасета с master="yarn". (#154)

Когда приложение spark отправляет lineage поле proxyUrl может приходить в такой форме:

http://node-mn-0001.msk.mts.ru:8088/proxy/application_1733,http://node-mn-0002.msk.mts.ru:8088/proxy/application_7400

Мы используем только первый (до ,)

  • Добавлен парсинг имени набора данных для удаления части, похожей на партицию, из имени. (#175)

=== Было

Два разных набора данных:

Dataset(name="/app/warehouse/somedb.db/sometable/business_dt=2025-01-01/reg_id=99")
Dataset(name="/app/warehouse/somedb.db/sometable/business_dt=2025-02-01/reg_id=99")

=== Стало

Объединение двух партиций в один набор данных:

Dataset(name="/app/warehouse/somedb.db/sometable")
  • Изменена логика для схемы выходного/входного набора данных в ответе происхождения. (#185)

Добавлены типы схемы в ответе 'EXACT_MATCH' и 'LATEST_KNOWN'.

'EXACT_MATCH' - когда последний и первый (по порядку created_at по возрастанию) schema_ids одинаковы. 'LATEST_KNOWN' - когда последний и первый не совпадают, в этом случае возвращается последний schema_id.

Примеры набора данных в ответе происхождения

=== Было

{
    "relations": {
        "direct_column_lineage": [],
        "indirect_column_lineage": [],
        "inputs": [
            {
                "from": {
                    "id": "2697",
                    "kind": "DATASET"
                },
                "last_interaction_at": "2025-03-14T15:22:30.572000Z",
                "num_bytes": 13166146,
                "num_files": 240,
                "num_rows": 22793,
                "schema": {
                    "fields": [
                        {
                            "description": null,
                            "fields": [],
                            "name": "dt",
                            "type": "timestamp"
                        },
                        {
                            "description": null,
                            "fields": [],
                            "name": "customer_id",
                            "type": "decimal(20,0)"
                        },
                        {
                            "description": null,
                            "fields": [],
                            "name": "total_spent",
                            "type": "float"
                        }
                    ],
                    "id": "1418"
                },
                "to": {
                    "id": "1260",
                    "kind": "JOB"
                }
            },
            {
                "from": {
                    "id": "3300",
                    "kind": "DATASET"
                },
                "last_interaction_at": "2025-03-17T08:45:58.439000Z",
                "num_bytes": 13060345,
                "num_files": 112,
                "num_rows": 13723,
                "schema": null,
                "to": {
                    "id": "0195a347-fa5f-7a72-aa14-bc510fadfd3a",
                    "kind": "RUN"
                }
            }
        ]
    }
}

=== Стало

{
    "relations": {
        "direct_column_lineage": [],
        "indirect_column_lineage": [],
        "inputs": [
            {
                "from": {
                    "id": "2697",
                    "kind": "DATASET"
                },
                "last_interaction_at": "2025-03-14T15:22:30.572000Z",
                "num_bytes": 13166146,
                "num_files": 240,
                "num_rows": 22793,
                "schema": {
                    "fields": [
                        {
                            "description": null,
                            "fields": [],
                            "name": "dt",
                            "type": "timestamp"
                        },
                        {
                            "description": null,
                            "fields": [],
                            "name": "customer_id",
                            "type": "decimal(20,0)"
                        },
                        {
                            "description": null,
                            "fields": [],
                            "name": "total_spent",
                            "type": "float"
                        }
                    ],
                    "id": "1418",
                    "relevance_type": "EXACT_MATCH" // <--
                },
                "to": {
                    "id": "1260",
                    "kind": "JOB"
                }
            },
            {
                "from": {
                    "id": "3300",
                    "kind": "DATASET"
                },
                "last_interaction_at": "2025-03-17T08:45:58.439000Z",
                "num_bytes": 13060345,
                "num_files": 112,
                "num_rows": 13723,
                "schema": {
                    "fields": [
                        {
                            "description": null,
                            "fields": [],
                            "name": "dt",
                            "type": "timestamp"
                        },
                        {
                            "description": null,
                            "fields": [],
                            "name": "customer_id",
                            "type": "decimal(20,0)"
                        },
                        {
                            "description": null,
                            "fields": [],
                            "name": "total_spent",
                            "type": "float"
                        }
                    ],
                    "id": "1657",
                    "relevance_type": "LATEST_KNOWN" // <--
                },
                "to": {
                    "id": "0195a347-fa5f-7a72-aa14-bc510fadfd3a",
                    "kind": "RUN"
                }
            }
        ]
    }
}
  • Удалено имя партиции из имен наборов данных. (#188)
  • Удалены наборы данных и символические ссылки из ответа lineage, которые не имеют входов или выходов. (#189)
  • Добавлена конечная точка /v1/auth/logout в KeycloakAuthProvider. (#192)