Как движки вас газлайтят?
Расскажу историю, которая на днях прозошла на нашем кластере
💋Что надо сделать? Создать табличку и запустить пайплайн заливки данных. Казалось бы - что может быть проще?
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