Создание новых материализаций
Введение
Материализации моделей, с которыми вы знакомы, такие как table, view и incremental, реализованы как макросы в пакете, который распространяется вместе с dbt. Вы можете ознакомиться с исходным кодом этих материализаций. Если вам нужно создать свои собственные материализации, чтение этих файлов — хорошее начало. Продолжайте читать ниже для глубокого погружения в материализации dbt.
Это продвинутая функция dbt. Дайте нам знать, если вам нужна помощь! Мы всегда рады пообщаться.
Создание материализации
Блоки materialization позволяют dbt загружать пользовательские материализации из пакетов. Эти блоки работают во многом так же, как и блоки macro, но с несколькими ключевыми отличиями. Материализации определяются следующим образом:
{% materialization [имя материализации], ["указанный адаптер" | default] %}
...
{% endmaterialization %}
Материализациям можно дать имя, и они могут быть привязаны к конкретному адаптеру. dbt выберет материализацию, привязанную к текущему используемому адаптеру, если она существует, или вернется к default адаптеру. На практике это выглядит так:
{% materialization my_materialization_name, default %}
-- кросс-адаптерная материализация... предположим, что Redshift не поддерживается
{% endmaterialization %}
{% materialization my_materialization_name, adapter='redshift' %}
-- переопределение материализации для Redshift
{% endmaterialization %}
Способность dbt динамически выбирать правильную материализацию на основе активной базы данных называется множественной диспетчеризацией. Эта функция открывает целый мир возможностей для кросс-базовой совместимости — если вам это интересно, пожалуйста, дайте нам знать в Slack!
Структура материализации
Материализации отвечают за то, чтобы взять SQL-выражение модели dbt и превратить его в преобразованный набор данных в базе данных. Как правило, материализации имеют следующую структуру:
- Подготовить базу данных для новой модели
- Выполнить pre-hooks
- Выполнить весь SQL, необходимый для реализации требуемой материализации
- Выполнить post-model hooks
- Очистить базу данных при необходимости
- Обновить кэш Relation
Каждая из этих задач объясняется в разделах ниже.
Подготовка базы данных
Материализации отвечают за создание новых таблиц или представлений в базе данных, а также за вставку, обновление и удаление данных в существующих таблицах. Соответственно, материализациям необходимо знать текущее состояние базы данных, чтобы точно определить, какой SQL им нужно выполнить. Ниже приведён пример псевдокода для этапа «настройки» материализации table:
-- Обратитесь к материализации таблицы (ссылка выше) для примера реального синтаксиса
-- Этот код не будет работать и предназначен только для демонстрации
{% set existing = adapter.get_relation(this) %}
{% if existing and existing.is_view %}
{% do adapter.drop_relation(existing) %}
{% endif %}
В этом примере метод get_relation используется для получения состояния текущей выполняемой модели из базы данных. Если модель существует как представление, то представление удаляется, чтобы освободить место для таблицы, которая будет построена позже в материализации.
Это упрощенный пример, и фаза настройки для материализации может стать довольно сложной! При создании материализации обязательно учитывайте состояние базы данных и любые предоставленные флаги (например, --full-refresh), чтобы гарантировать, что код материализации ведет себя правильно в различных сценариях.
Запуск pre-hooks
Pre- и post-hooks могут быть указаны для любой модели — убедитесь, что ваша материализация корректно работает с этими настройками. Две переменные, pre_hooks и post_hooks, автоматически внедряются в контекст материализации. Вызывайте эти хуки в нужное время с помощью:
...
{{ run_hooks(pre_hooks) }}
....
Выполнение SQL
Создайте свою материализацию DML, учитывая различные варианты существования table, флаги материализации и т.д. Существует ряд функций адаптера и контекстных переменных, которые могут помочь вам в этом. Обязательно обратитесь к разделу Reference на этом сайте для полного списка доступных переменных и функций.
Запуск post-hooks
См. раздел выше о pre-hooks для получения дополнительной информации о запуске post-hooks.
Очистка
Фаза "очистки" материализации обычно переименовывает или удаляет отношения и фиксирует транзакцию, открытую на этапе "подготовки" выше. Например, материализация table выполняет следующий код очистки:
{{ drop_relation_if_exists(backup_relation) }}
Обязательно commit транзакцию на этапе cleanup материализации с помощью {{ adapter.commit() }}. Если вы не зафиксируете эту транзакцию, она будет отменена dbt, и преобразования, примененные в вашей материализации, будут отброшены.
Обновление кеша Relation
Материализации должны возвращать список отношений, которые они создали в конце выполнения. dbt использует этот список отношений для обновления кеша отношений, чтобы уменьшить количество запросов, выполняемых против information_schema базы данных. Если список отношений не возвращается, dbt выдаст предупреждение о депрекации и определит созданное отношение из настроенной базы данных, схемы и псевдонима модели.
{%- materialization my_view, default -%}
{%- set target_relation = api.Relation.create(
identifier=this.identifier, schema=this.schema, database=this.database,
type='view') -%}
-- ... настройка базы данных ...
-- ... запуск pre-hooks...
-- построение модели
{% call statement('main') -%}
{{ create_view_as(target_relation, sql) }}
{%- endcall %}
-- ... запуск post-hooks ...
-- ... очистка базы данных...
-- Возвращение отношений, созданных в этой материализации
{{ return({'relations': [target_relation]}) }}
{%- endmaterialization -%}
Если материализация создает только одно отношение, то возвращение этого отношения в конце материализации достаточно для синхронизации кеша Relation dbt. Если материализация переименовывает или удаляет отношения, отличные от отношения, возвращаемого материализацией, то требуется дополнительная работа для поддержания кеша в синхронизации с базой данных.
Чтобы явно удалить отношение из кеша, используйте adapter.drop_relation. Чтобы явно переименовать отношение в кеше, используйте adapter.rename_relation. Вызов этих методов предпочтительнее выполнения соответствующего SQL напрямую, так как они изменяют кеш по мере необходимости. Если вам нужно выполнить SQL для удаления или переименования отношений напрямую, используйте методы adapter.cache_dropped и adapter.cache_renamed для синхронизации кеша.
Конфигурация материализации
Материализации поддерживают пользовательскую конфигурацию. Вы, возможно, знакомы с некоторыми из этих конфигураций из материализаций, таких как unique_key в инкрементальных моделях или strategy в снимках.
Указание параметров конфигурации
Конфигурации материализации могут быть "необязательными" или "обязательными". Если пользователь не предоставляет обязательные конфигурации, dbt выдаст ошибку компиляции. Вы можете определить эти параметры конфигурации с помощью функций config.get и config.require.
# необязательный
config.get('optional_config_name', default="the default")
# обязательный
config.require('required_config_name')
Для получения дополнительной информации о функции config dbt Jinja см. справочник config.
Приоритет материализации
dbt выберет макрос материализации в следующем порядке (нижний имеет приоритет):
- глобальный проект - по умолчанию
- глобальный проект - специфичный для плагина
- импортированный пакет - по умолчанию
- импортированный пакет - специфичный для плагина
- локальный проект - по умолчанию
- локальный проект - специфичный для плагина
В каждом из указанных пространств поиска материализация может быть определена только один раз. Два разных импортированных пакета не могут предоставлять одну и ту же материализацию - будет выдана ошибка.
Конкретные материализации можно выбрать, используя точечную нотацию при выборе материализации из контекста.
Мы рекомендуем не переопределять имена материализаций напрямую, а вместо этого использовать префикс или суффикс, чтобы обозначить, что материализация изменяет поведение реализации по умолчанию (например, my_project_incremental).