"Как движки вас газлайтят? Расскажу историю, которая на днях прозошла на нашем кластере 💋Что надо сделать? Создать табличку и запустить пайплайн заливки данных. Казалось бы - что может быть проще? 1. Создаем таблицу в Trino: ``` CREATE TABLE iceberg.raw.first_table ( my_beautiful_column varchar ) WITH ( format = 'PARQUET', format_version = 2 ) ``` 2. Запускаем пайплайн - создается веточка: ``` ALTER TABLE raw.first_table CREATE OR REPLACE BRANCH my_branch ``` ``` SELECT * FROM iceberg.raw.""first_table$refs"" --main BRANCH --my_branch BRANCH ``` Пайплайн падает, не успев записать данные. Но я правлю ошибку и перезапускаю - теперь все прекрасно 🙌 Ужастики так-то тоже начинаются с размеренной жизни Но в один момент я создаю таблицу через Spark: ``` CREATE TABLE raw.second_table ( my_beautiful_column STRING ) USING iceberg ``` Снова запускаю пайплайн, и он снова падает. Я правлю ошибку и перезапускаю... ``` Exception in thread ""main"" java.lang.IllegalArgumentException: Cannot complete replace branch operation on raw.second_table, main has no snapshot ``` Что случилось? Таблица точно такая же, код точно такой же. Просчитался, но... где? 🤔 😑 Тут начинаются раскопки. Создаю табличку через трино и через спарк, иду на s3 смотреть файл с метаданными Оказывается, что трино создает отдельный снепшот с добавлением 0 строк: 🤍""current-snapshot-id"" : 677178324561195060 🤍таблица raw.first_table.history имеет 1 строку А спарк такого не делает - он их создает, только когда данные меняются, компактятся: 🤍""current-snapshot-id"" : -1 🤍таблица raw.second_table.history пустая Теперь идем в репку айсберга, а там такое: ``` if (create && replace && !refExists) { safeCreateBranch() } else if (replace) { Preconditions.checkArgument(snapshotId != null, ""Cannot complete replace branch operation on %s, main has no snapshot"", ident) manageSnapshots.replaceBranch(branch, snapshotId) } ``` 🩵Какой ход событий получается: 1. Попадаем в условие if: create есть, replace есть, ветки еще нет. Ветка создается 2. Пытаемся записать данные в ветку 3. Код падает, а ветка остается(!) 4. Перезапускаем и в условие if уже не попадаем - ветка уже есть 5. Попадаем в else if (replace) - вспоминаем, что через спарк никакой снепшот не создается, поэтому snapshotId у нас null ➡️ Как решаем проблему Перед созданием ветки нужно дропнуть существующую, если вдруг пайплайн упал и не успел почистить ветку: ``` ALTER TABLE raw.second_table DROP BRANCH IF EXISTS my_branch ``` Либо добавить catch/except, который дропнет ее в самом конце Общий вывод такой: при работе с разными движками любой кусок может работать по-разному. Даже самая простейшая штука, которую вы не ожидали🥺 @data_engineerette"