Как мы построили собственный коннектор Qlik → OpenMetadata, почему коробочный не сработал, и как парсер скриптов стал ключевым компонентом.

1. Проблема: BI-инфраструктура как «чёрный ящик»

У нас — более 1000 дашбордов в Qlik.
Но:

  • «А откуда эти цифры?»

  • «Кто отвечает за этот KPI?»

  • «А этот дашборд вообще ещё нужен?»

Факты:

  • Каталог данных — пустой

  • Lineage — обрывается на границе Qlik

  • Глоссарий — не связан с витринами

  • Владельцы, источники, даты обновления — неизвестны

Цель: что должен делать коннектор

Нам нужен не просто «мост», а понимание BI. Коннектор должен :

Функция

Зачем

Актуальный каталог

Чтобы найти дашборд по названию метрики за 2 минуты 

Lineage «от таблицы до графика»

Чтобы при ошибке в источнике быстро найти все затронутые отчёты

Привязка к глоссарию

Чтобы связать «Выручка по регионам» с термином Gross Revenue

Удаление устаревших объектов

Чтобы не тратить ресурсы на поддержку «фантомов»

Почему не подошёл коробочный коннектор

OpenMetadata предоставляет коннектор qliksense. Мы протестировали его и быстро поняли: он не решает нашу задачу.

  1. Что он умеет:
    - Извлекать дашборды, графики, модели данных
    - Строить lineage по tableQualifiers (если заполнены)

  2. Чего не хватает критически:

    Проблема

    Последствия

    Нет парсинга скриптов

    Lineage = null для 80% приложений (скрипты в SQL, а не tableQualifiers)

    Нет include-файлов

    Скрипты обрезаны → lineage обрывается

    Нет миграции экстракторов

    Lineage ведёт к «пустому» аппу без графиков

    Нет глоссария

    Невозможно найти дашборд по KPI

    Нет DQ-валидации

    Падает в production — узнаём через день

Вывод: коннектор создаёт записи в каталоге. Но ни у одной нет lineage к БД, и ни одна не привязана к глоссарию.
Пользователи не доверяют каталогу. Это хуже, чем было ранее.

Архитектура: не «коннектор», а фабрика метаданных

Мы перестали думать о «коннекторе» и начали проектировать систему, которая:

Qlik → [извлечение] → Business Views → [валидация] → OpenMetadata

Ключевая абстракция — Business Views

class BusinessApp:  
  id: str  
  name: str    
  owner: Optional[str] # из Jira / AD    
  stream: str    
  jira_ticket: Optional[str]    
  script: str   уже с раскрытыми include-файлами    
  measures_id: Set[str]  # для привязки к глоссарию    
  datasources: List[BusinessDatasource]

→ Теперь можно тестировать логику без Qlik.
→ Можно обогащать из внешних источников.
→ Можно подменять данные для отладки.

Сердце системы: собственный анализатор скриптов qlik_script

Без понимания скрипта — нет lineage. А стандартные парсеры (sqlparse, sqlfluff) ломаются на Qlik-DSL:

LET vPath = 'path/to/';
FOR EACH file IN $(vPath)*.qvd    
  LOAD * FROM [$(file)] (qvd);
NEXT

Что мы построили:

  • Regex-based Sequential State Machine Interpreter (не AST!)

  • Последовательная обработка команд через pattern matching

  • Поддержка:
    - $(include=...) — рекурсивная загрузка
    - $(var) — мультиэтапная подстановка с защитой от зацикливания
    - комментарии всех форматов (//, /* */, REM, --, Trace)
    - мультиалгоритмический поиск measure_id (CASE, WHERE, переменные, match(), inline-LOAD)

Результат:

Метрика

Значение

Точность measure_id

~85% (60% — по скрипту, 25% — по комментариям)

Покрытие таблиц

~90% через интерпретацию + 5–10% через regex-поиск

Время обработки скрипта

< 300 мс (200 строк + 2 include-файла)

Теперь lineage ведёт не к data_model_name, а к конкретным таблицам БД.

Production-запуск: где всё пошло не так — и как быстро починили

Проблема 1: MaxSessionsExceedError

Причина: лимит WebSocket-сессий в Qlik.
Решение:

@retry_with_backoff(exceptions_to_repeat=(MaxSessionsExceedError,))

def establish_connection(self, app_id: str): 

# закрываем старое соединение → создаём новое

Проблема 2: AccessDenied на 20% приложений

Решение:

  • Graceful degradation: пропускаем, логируем, считаем метрику

  • Алерт в Zabbix, если access_denied_ratio > 0.25

Проблема 3: Экстракторы ≠ визуализации

Суть: данные готовит app_extractor, показывает — app_viz.
Решение:

  • Находим app_extractor (без листов) и app_viz (с листами) в одном stream

  • Если в app_viz есть lib://app_extractor/... → переносим datasources от app_extractor к app_viz

    → Теперь lineage ведёт к дашборду, который пользователь реально видит.

Итог: что получилось

Метрика

Было

Стало

Поиск дашборда по KPI

2 часа

2 минуты

Lineage к таблицам БД

0%

85%+

Привязка к глоссарию

0%

60%+

Вывод��

  • Готовые коннекторы — для демо. Для production — только кастом.

  • Lineage без скриптов — фикция. Если не парсите SQL — не тратьте время.

  • Business Views — must-have. Без доменной модели — спагетти-код.

  • DQ — не «когда-нибудь», а «с первого коммита». Иначе коннектор — бомба с часовым механизмом.

  • Graceful degradation > идеальная функциональность. Лучше 95% аппов с lineage, чем 100% без него.