Предистория Используемые технологии Подготовка стенда Теперь всё готово для работы над проектом Создание SCD-таблицы Создание инструмента для наполнения SCD-таблицы Генерация DAG через SCD-таблицу Заключение Здесь будет рассказано почему это всё получилось и коротко про бизнес-кейс, если не интересно, то переходи сразу к следующему разделу :) Существуют такие метрики, которые выглядят просто, но очень хорошо отображают суть бизнеса в какой-то момент. Такие метрики как: MAU, DAU, сумма продаж и прочее дают стейкхолдером хорошее понимание что сейчас с бизнесом. Для дата-аналитика это выглядит так: дата, название метрики, значение. Для дата-инженера это выглядит как семь кругов ада: извлечение из источника, очистка, обогащение, агрегация, расчет бизнес-логики, снова агрегация, опять очистка и только тогда метрика готова попасть "на стол". (количество кругов ада различается от команды к команде и от бизнес-задачи, но суть вы поняли :)) Я постоянно пытаюсь как-то автоматизировать свою работу и упростить жизнь и себе, и людям. И тут появился хороший шанс это сделать. Пришел бизнес с примерно таким запросом: "У нас существует более ста метрик, которые имеют вид: дата, название метрики, значение. Мы хотели бы это всё автоматизировать, потому что это собирается частично руками, частично какими-то скриптами и у этого всего разные владельцы. А мы хотим всё хранить в одном месте. Ну и самое главное – мы хотим версионировать все свои метрики, чтобы мы знали когда, зачем и почему изменилась та или иная метрика" – на самом этот запрос звучал так: "нам нужно хранить метрики и знать когда, что и почему с ними происходило" :) И я начал думать. На ум пришла сразу же идея – использовать SCD.  Сразу захотелось создать такую таблицу с типом "II", в которой будет метрика, логика, комментарии и прочее. У каждой строки будет дата создания, дата "удаления". А ещё я очень хотел не только создать такую таблицу, но и иметь возможность генерировать DAG через эту таблицу И я это сделал. Об этом и пойдет дальше речь. Если что, то  весь код, все коммиты и прочее находятся в данном git-репозитории. MacBook Air (M1, 2020) Оперативная память 16 Gb macOS Monterey 12.6 Docker 4.22.0 Docker resources: CPUs: 2 Memory: 4 Gb Swap: 4 Gb Python3.11 Для начала скачаем себе docker-compose.yaml с официального сайт Airflow: Если его запустить, то мы получим там Python3.7. Но хотелось бы что-то побыстрее, поэтому давайте сделаем себе Python3.11. Для этого нужно создать Dockerfile со следующей командой: Затем в docker-compose.yaml изменяем стандартный образ на наш (строка 53): Всё, готово. Теперь давайте соберем наш проект. Для этого выполним команду: * Если вы уже выполняли команду docker-compose up -d, то необходимо сначала выполнить команду docker-compose build и затем снова docker-compose up -d. Ждем, когда скачаются все образы и поднимутся все контейнеры. После сборки Airflow он будет доступен по адресу http://0.0.0.0:8080/home Для этого нужно выполнить команду: В наше виртуальное окружение установятся все зависимости, которые необходимы для работы данного проекта. Для нашего проекта понадобится база данных (БД). Я по классике выбрал PostgreSQL (в оригинальном проекте был развернут инстанс GreenPlum 6). Для того, чтобы корректно работала БД в нашей сборке мы добавим в конец docker-compose.yaml следующую команду: После добавления этой команды необходимо заново перезапустить сборку командой: Для начала напишем простой DAG, который позволит проверить работу коннекта к нашей ранее созданной БД.  Запустим его и посмотрим на него. Если раны горят зеленым, то всё идет по плану. Далее давайте напишем наш типовой DAG, который будет собирать метрики по типу: дата, метрика, значение.  В генерируемых значениях есть уже колонка kpi_id. Пока не обращаем на неё внимание. О ней чуть позже. Перед запуском DAG давайте создадим таблицу, в которую будут писаться метрики.  После этого мы можем запустить DAG и мы увидим как появляются данные в нашей таблице. Уже хорошо. Мы, на текущий момент, имеем DAG, который собирает какую-то метрику (давайте примем за условность, что метрика реально собирается, хоть и значения рандомного генерируются). Теперь мы можем перейти к созданию SCD-таблицы с метриками. Немного повторюсь, я хочу в этой таблице хранить всю информацию, которая касается метрик. Соответсвенно я хотел бы иметь полную информацию о ней: название, бизнес-логику, комментарии аналитиков, комментарии дата-инженеров и прочее.  А самое главное – я хочу из этой информации генерировать DAG, поэтому нужно определить значения, которые не могут быть пустыми (NOT NULL). Давайте создадим такую таблицу следующим скриптом:  И давайте сразу же создадим таблицу, в которой будут храниться все наши собранные метрики:  Ниже я покажу MVP-вариант, который может заполнять SCD-таблицу. На своем проекте, с помощью другой команды, мы прикрутили к нему веб-интерфейс.  И теперь давайте вызовем нашу основную функцию scd_dim_kpi, чтобы получить первую запись с метрикой в нашей таблице.  Вот что мы увидим в БД: Отлично, метрика находится в БД и с этого момента можно сказать начинается её версионирование. Каждое изменение будет фиксироваться в БД отдельной строкой и мы сможем отследить все изменения. Давайте для примера изменим поле comment_de: Получим в БД такую информацию: kpi_id не изменился, изменилась информация в dim_kpi_dag_gen_config и также видно, когда была изменена метрика, ну и самое главное – флаг is_actual переместился на последнюю запись по данному kpi_id. Теперь мы готовы генерировать DAG из этой таблицы. Давайте оставим simple_dag без изменений, но возьмем его за основу и сделаем из него функцию, которая будет возвращать DAG на основании полученных атрибутов.  После добавления данного DAG мы сразу же видим результат – в нашем веб-интерфейсе появился DAG с ранее созданной метрикой: Давайте запустим наш сгенерированный DAG и посмотрим на результат, но в начале отредактируем SQL-запрос под необходимый нам формат.  Всё, DAG работает и что-то собирает. Давайте сделаем для примера ещё несколько DAG. Я создал пять дополнительных DAG по следующему шаблону (для демонстрации):  Всё работает корректно. В БД записи есть: В Airflow DAG корректно отображаются и работают. Давайте добавим ещё сенсоры для демонстрации. Я добавил новый DAG и если посмотреть на его граф, то мы можем увидеть EmptyOperator – empty_0. Сейчас это просто заглушка на случай если у нас нет сенсоров на другие DAG. При дагране он выполнится менее чем за секунду и наш пайплайн продолжит корректно работать. Давайте добавим список сенсоров:  И получим такой граф. Теперь вместо нашей заглушки мы имеем список сенсоров, которые будут проверять отработали ли какие-то DAG или нет. Ну, и в конце давайте посмотрим, что насобирали наши сгенерированные DAG: В дальнейшем мы сможем сделать JOIN между таблицей фактов и справочником, чтобы получить информацию по каждой из метрик. Я постарался описать максимально подробно свой кейс. Возможно, он является узконаправленным, но тут я рассказал об очередном генераторе DAG. Также я не отрицаю, что это не идеально, но и идеала не существует ;) Мог какие-то моменты упустить/упростить/не описать подробно. За всем этим можете обращаться в комментарии. Важные моменты, на которые стоит обратить внимание:  Данная реализация не генерирует файл, а генерирует только объекты, поэтому во всех DAG будет исходный код нашего DAG, в котором происходит вся генерация. Если вам нужны физически созданные DAG, то можете прочитать мою предыдущую статью. Если некорректно передать какие-то атрибуты в функцию, то сломаются все DAG. Если к примеру вместо даты попадет какая-то строка, то произойдет исключение, функция не отработает и ни один DAG не будет создан. Но вы можете это улучшить при помощи обработки исключений и прочего. В данной реализации kpi_id нужно подставлять самостоятельно. В моем варианте не реализован автоинкремент. Но как я говорил ранее этот продукт доработан и  другая команда реализовала уже это на backend. Я показал самую простую реализацию данного инструмента. Вы можете настраивать его под себя, с обработкой каждого атрибута, использовать фреймворки, использовать самописные обработчики и прочие. Можно сделать дополнительную нормализацию данных, сделать дополнительные справочники, привязать ORM-модели и реализовать сразу какой-то backend, чтобы не заполнять это руками. В демонстрационном DAG реализована идемпотентность, которая позволяет грузить метрики снапшотами за каждый день. Вы можете это регулировать также под свои нужды. Мой кейс решает определенную бизнес-задачу. Бизнес рад и я рад :)   Data Engineer