Перейти к основному содержимому

Почему моя модель пересоздаётся, а не используется повторно?

dbt State решает, можно ли повторно использовать модель, разбирая SQL-код, полученный после рендеринга, в синтаксическое дерево и сравнивая хеш. Если хеш изменился (что указывает на изменение логики модели), dbt State пересоздаёт модель.

Для dbt State безопасность и точность имеют приоритет: если нельзя гарантировать, что пропуск узла безопасен, узел пересоздаётся. На этой странице перечислены несколько распространённых ситуаций, которые приводят к избыточному пересозданию моделей, а также рекомендации по увеличению доли повторного использования.

Следующие ситуации часто приводят к неожиданному пересозданию моделей:

Представления с select *​

dbt State повторно использует модель, когда хеш её скомпилированного SQL-кода совпадает с сохранённым хешем. Если представление использует select * непосредственно для ref() или source(), dbt не может определить список столбцов на этапе разбора — с момента последнего запуска в вышестоящей модели или исходной таблице могли появиться или исчезнуть столбцы. Чтобы избежать риска, dbt State принудительно пересоздаёт модель.

Например, это представление будет пересоздано, даже если stg_orders не изменилась, поскольку на этапе разбора dbt не может определить, остались ли в stg_orders те же столбцы, что и раньше:

-- stg_orders_view.sql (materialized: view)
select * from {{ ref('stg_orders') }}

Однако если использовать select * для CTE, dbt может определить столбцы из определения CTE и безопасно использовать представление повторно:

with renamed as (
select order_id, customer_id, order_total from {{ ref('stg_orders') }}
)

select * from renamed

Если в CTE явно указаны имена столбцов, запрос select *, читающий из этого CTE, не приведёт к принудительному пересозданию, даже если в предыдущем CTE использовался select * для ref() или source(). Модель с типичной структурой слоя staging будет использоваться повторно:

with source as (
select * from {{ source('jaffle_shop', 'orders') }}
),

renamed as (
select
id as order_id,
user_id as customer_id,
amount as order_total
from source
)

select * from renamed
подсказка

Чтобы избежать принудительного пересоздания, явно указывайте имена столбцов при выборке непосредственно из ref() или source(). Также можно исключить представления из выполнения с помощью --exclude config.materialized:view.

Недетерминированные шаблоны Jinja​

Некоторые макросы и переменные окружения могут приводить к неожиданному пересозданию моделей. Например, dbt_utils.get_relations_by_pattern (макрос для анализа метаданных) в сочетании с dbt_utils.union_relations может возвращать объекты базы данных в разном порядке при каждом запуске, формируя разный SQL-код после рендеринга, даже если логика проекта не изменилась. Аналогично, переменные окружения, значения которых меняются между запусками, приводят к формированию разного SQL-кода после рендеринга при каждом запуске:

select '{{ env_var("AIRFLOW_RUN_ID") }}' as airflow_run_id, ...

Поскольку порядок результатов запроса или значение переменной окружения меняется, хеш SQL-кода после рендеринга отличается от сохранённого хеша при каждом запуске. dbt State считает это изменением кода и пересоздаёт модель, хотя основная логика проекта не изменилась. Эта ситуация может затронуть модели любого типа, а не только представления: если базовая модель или модель слоя staging пересоздаётся при каждом запуске, все зависящие от неё модели также пересоздаются.

Чтобы избежать этих ненужных пересозданий, включите compare_unrendered_code. При включённом параметре dbt State проверяет и шаблон Jinja, и SQL-код после рендеринга; недетерминированные значения, которые не меняют шаблон, не вызывают пересоздания. Например:

{{ config(state={"compare_unrendered_code": true}) }}

select '{{ env_var("AIRFLOW_RUN_ID") }}' as airflow_run_id, ...

Модели с внешними источниками в BigQuery​

В BigQuery модели, использующие внешние источники (например, Google Sheets), всегда пересоздаются, поскольку BigQuery не предоставляет временные метки изменения внешних источников, и dbt State не может определить актуальность данных.

подсказка

Чтобы внешние источники не считались устаревшими при каждом запуске, настройте loaded_at_field или loaded_at_query в определении источника, указав поле с временной меткой. Это позволит dbt State запрашивать поле с временной меткой напрямую для определения актуальности данных, а не полагаться на метаданные хранилища.

Модели с пользовательскими материализациями​

Модели с пользовательскими материализациями всегда создаются заново и никогда не используются повторно. Пользовательские материализации могут иметь побочные эффекты (например, изменять свойства таблиц или записывать данные в другие схемы), и dbt State не может с уверенностью определить, приведёт ли пропуск выполнения к тому же результату.

Как провести диагностику​

После запуска используйте (Версии dbt: v2.0 и новее) dbt state explain, чтобы узнать, почему dbt State пересоздал, повторно использовал или клонировал конкретную модель. Для подробного разбора используйте флаг --verbose вместе с -s, чтобы выбрать нужную модель:

примечание

Название команды зависит от версии: в dbt v2 используется dbt state explain (с пробелом), а в dbt v1 — dbt-state explain (с дефисом).

(Версии dbt: v2.0 и новее)
dbt state explain --verbose -s my_model_name

Если вы используете dbt platform, та же информация доступна без выполнения команды — перейдите на вкладку Explain на странице сведений о запуске задания, чтобы увидеть полный разбор принятого решения для каждого узла.

Нашли ошибку?

0
Loading