fix(course): исправлена семантика Iceberg snapshots

- Зачем: объяснения CREATE OR REPLACE и rollback расходились с поведением Iceberg 1.10.0 и ломали повторный запуск упражнений.
- Что: исправлены материалы и планы модулей 2, 4, 5, 7 и 8; одноразовые таблицы сбрасываются через DROP TABLE PURGE.
- Проверка: JSON и Python AST валидны; на Spark 3.5.1, Iceberg 1.10.0, JDBC/MinIO и Trino 478 подтверждены rollback, replace, time travel и повторный сброс до одного snapshot-а.
This commit is contained in:
ddadmin committed 2026-09-15 07:35:39 -04:00
1 parent ff474f05f6
commit a085b2fc55
11 files changed
+106 -76

No files matched your search

+7 -6
View File
@@ -1,7 +1,7 @@
# Модуль 4. Первая рабочая Iceberg-таблица и слой bronze
**Статус:** `Draft`
**Последнее обновление:** `2026-03-07`
**Последнее обновление:** `2026-09-15`
## Цель
@@ -50,11 +50,12 @@
### Создание и наполнение таблицы: CTAS с явным списком колонок
Используем `CREATE OR REPLACE TABLE ... USING iceberg AS SELECT col1, col2, ... FROM raw_view` (CTAS). Это:
Используем `CREATE OR REPLACE TABLE ... USING iceberg AS SELECT col1, col2, ... FROM raw_view` (CTAS). В настроенном `SparkCatalog` это атомарная замена. Она:
- объединяет создание и наполнение в один шаг — понятнее и ближе к `CREATE TABLE AS SELECT` в PostgreSQL;
- явный список колонок в SELECT исключает ошибки из-за несовпадения порядка или состава колонок (PRD Risk #3, #5);
- `CREATE OR REPLACE` обеспечивает идемпотентность: повторный запуск пересоздаёт таблицу, snapshot history остаётся чистой;
- trade-off: `CREATE OR REPLACE` уничтожает историю — в production это опасно, но для первичной загрузки учебного bronze допустимо. В Модуле 7 студент увидит, почему в рабочих сценариях нужен другой подход.
- повторный запуск приводит текущее содержимое к тому же результату и не дублирует строки;
- создаёт новый snapshot и сохраняет прежние snapshot-ы для time travel;
- заменяет текущие данные, схему и partition spec; новый snapshot не имеет `parent_id`, поэтому прежние snapshot-ы перестают быть предками текущего состояния. Отдельная именованная ветка при этом не создаётся.
В Секции 2 DDL с явной схемой показывается как справочный пример (не выполняется) — чтобы студент увидел знакомую форму `CREATE TABLE (col type, ...)`.
@@ -105,7 +106,7 @@ Bronze нужен Модулю 5. Spark-сессия останавливает
### Секция 3: Загрузка raw → bronze (CTAS)
- **[md]** Аналогия с `CREATE TABLE ods AS SELECT ... FROM stg` в Greenplum. Почему явный список колонок: `SELECT *` привязывается к порядку, а не к именам — это хрупко и может дать некорректное сопоставление полей. Используем `CREATE OR REPLACE TABLE ... AS SELECT col1, col2, ... FROM raw_yellow` — CTAS одновременно создаёт таблицу и наполняет данными.
- **[md]** Идемпотентность: `CREATE OR REPLACE` при повторном запуске пересоздаёт таблицу с чистого листа — snapshot history остаётся чистой (один snapshot = одна загрузка). Trade-off: в production `CREATE OR REPLACE` — разрушительная операция (удаляет историю), но для первичной загрузки учебного bronze это осознанный выбор ради простоты. В Модуле 7 увидим, почему в рабочих сценариях нужен другой подход.
- **[md]** Повторяемость: `CREATE OR REPLACE` при повторном запуске атомарно заменяет текущее содержимое и не дублирует строки, но добавляет новый snapshot. Прежние snapshot-ы сохраняются для time travel и перестают быть предками текущего состояния. Trade-off: операция может заменить схему и partition spec, поэтому для учебной полной загрузки она удобна, а в рабочих инкрементальных сценариях требует осторожности. В Модуле 7 семантика разбирается подробнее.
- **[code]** Чтение raw → `createOrReplaceTempView("raw_yellow")` → `CREATE OR REPLACE TABLE lakehouse.bronze.nyc_taxi_yellow USING iceberg AS SELECT VendorID, tpep_pickup_datetime, ... FROM raw_yellow` (CTAS с явным списком колонок).
- **[code]** Верификация: count bronze vs count raw — должны совпасть.
@@ -216,7 +217,7 @@ Bronze нужен Модулю 5. Spark-сессия останавливает
- **Схема raw PARQUET.** Фиксируем колонки NYC Taxi 2024 в DDL. Если студент скачал другой год — DDL может не совпасть. Пояснение: придерживаться инструкции из `START_HERE.md`.
- **Column case sensitivity.** NYC Taxi PARQUET использует mixed case (`VendorID`, `PULocationID`). Iceberg хранит имена case-sensitive. DDL должен совпадать с именами в PARQUET. Проверить на стенде при реализации.
- **Мелкие файлы.** Spark может создать много мелких data files (по числу partitions). Нормально для Module 4 — тема compaction отложена до Модуля 8. Упомянуть, не решать.
- **Идемпотентность.** Используем `CREATE OR REPLACE TABLE ... AS SELECT` (CTAS) для повторных запусков — пересоздаёт таблицу целиком, snapshot history остаётся чистой. Это проще и надёжнее, чем `DELETE FROM` + `INSERT INTO` (который усложняет snapshot history и требует проверки поддержки `DELETE` на текущем стеке). Компромисс: `CREATE OR REPLACE` уничтожает предыдущие snapshots, но для учебного bronze это допустимо. В markdown явно объяснить этот trade-off.
- **Повторный запуск.** Используем `CREATE OR REPLACE TABLE ... AS SELECT` (CTAS): текущее содержимое заменяется атомарно и строки не дублируются. При каждом запуске появляется новый snapshot; предыдущие snapshot-ы сохраняются для time travel, но перестают быть предками текущего. В markdown явно объяснить этот trade-off и риск замены схемы и partition spec.
## Out of Scope