feat(course): add SparkSession setup practice to module 3
This commit is contained in:
1 parent
00942c255f
commit
7c5e976405
10 files changed
+523
-22
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# План развития курса «Lakehouse без магии»
|
||||
|
||||
Статус: подключение Spark исправлено; следующая работа — практика настройки сессии.
|
||||
Статус: подключение Spark исправлено; практика настройки сессии по #2 подготовлена и проверена, ожидает ревью.
|
||||
Последнее обновление: 2026-09-15.
|
||||
|
||||
Здесь собираем отзывы студентов, выбираем следующие улучшения и объясняем их порядок. Подробные задания, критерии приёмки и актуальный статус исполнения хранятся в [Gitea Issues](https://git.dementev.space/ddmitry/mini-lakehouse-lab/issues).
|
||||
@@ -36,7 +36,9 @@ Iceberg-таблицы. Результаты проверок и их огран
|
||||
|
||||
### 2. Добавить практику «Как настроен Spark в нашем стенде»
|
||||
|
||||
**Статус:** запланировано; можно приступать после завершения #1.
|
||||
**Статус:** готово к ревью по #2; зависимость #1 закрыта, её итог прочитан. Практика добавлена в начало модуля 3, PRD, программа и справочные материалы обновлены.
|
||||
|
||||
**Результат:** проверены демонстрация, заполненный шаблон и контрольное решение, повторные ячейки, настоящий перезапуск ядра, временная Iceberg-таблица и весь raw-ingest. Машинный прогон — 4 минуты 10 секунд; время самостоятельного прохождения студентом ещё не измерено. Подробности и ограничения — в [отчёте проверки](issue-2-spark-session-validation.md).
|
||||
|
||||
**Задача:** [#2 — практика базовой настройки SparkSession](https://git.dementev.space/ddmitry/mini-lakehouse-lab/issues/2).
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# Проверка практики SparkSession в модуле 3
|
||||
|
||||
Дата: 2026-09-15.
|
||||
Задача: https://git.dementev.space/ddmitry/mini-lakehouse-lab/issues/2.
|
||||
База: `00942c2`, результат закрытой [#1](https://git.dementev.space/ddmitry/mini-lakehouse-lab/issues/1) прочитан вместе с комментариями.
|
||||
|
||||
## Что проверялось
|
||||
|
||||
Фактические ячейки `notebooks/03_raw_ingest_and_first_read.ipynb` выполнялись
|
||||
через `jupyter_client.KernelManager` в контейнере `jupyter`, Spark 3.5.1.
|
||||
Менеджер запускал настоящее IPython-ядро и перезапускал его через
|
||||
`restart_kernel(now=True)`. Проверка не подменяла создание сессии общим helper-ом.
|
||||
|
||||
Порядок воспроизведения:
|
||||
|
||||
1. Поднять стенд и получить стандартный bundle по `START_HERE.md`.
|
||||
2. В новом ядре выполнить демонстрацию, вывод настроек, опыт с часовым поясом,
|
||||
повторный builder и остановку сессии.
|
||||
3. Заполнить три пропуска шаблона, выполнить его дважды и сравнить контекст
|
||||
и applicationId. Выполнить опубликованное контрольное решение
|
||||
(при проверке извлекалось из блока `session-solution`).
|
||||
4. Выполнить проверку master/appName и дважды создать, прочитать и удалить
|
||||
временную таблицу.
|
||||
5. Перезапустить ядро. Убедиться, что переменные `spark` и `CLUSTER_MASTER`
|
||||
отсутствуют; выполнить контрольное решение, проверку подключения и таблицы.
|
||||
6. Продолжить исходный raw-ingest до `spark.stop()`, заполнив самостоятельные
|
||||
упражнения с lookup, top-5 локаций и ещё одной аномалией.
|
||||
7. Отдельно проверить заполненный шаблон в новом ядре и повторную загрузку
|
||||
уже существующих raw-файлов.
|
||||
|
||||
## Результаты
|
||||
|
||||
| Проверка | Результат |
|
||||
| --- | --- |
|
||||
| Демонстрационная сессия | Master `spark://spark-master:7077`, appName `module-03-session-demo`, сумма 0…9 = 45 |
|
||||
| Настройки каталога | `SparkCatalog`, `JdbcCatalog`, URI PostgreSQL и warehouse соответствуют общему файлу; Hadoop endpoint `http://minio:9000`, path-style `true` |
|
||||
| Изменяемая настройка | `spark.sql.session.timeZone` допускает изменение; Unix-время 0 отображается как `1970-01-01 00:00:00` в UTC и `1970-01-01 03:00:00` в Москве |
|
||||
| Восстановление | В `finally` восстановлено исходное `Etc/UTC`, проверено через assert |
|
||||
| Повторный builder с другим appName | Тот же объект сессии и контекста, фактическое имя остаётся `module-03-session-demo` |
|
||||
| Создание после stop | Новое имя `module-03-raw-ingest-and-first-read`, master кластерный |
|
||||
| Повторение шаблона и контрольного решения | Контекст и applicationId не меняются |
|
||||
| Заполненный шаблон в отдельном новом ядре | Master и appName верны, распределённая сумма 0…9 = 45 |
|
||||
| Перезапуск ядра | Переменные исчезли, сессия создана заново; ID изменился с `app-20260915125347-0001` на `app-20260915125511-0002` |
|
||||
| Iceberg до и после перезапуска | Три независимые таблицы с уникальными именами; каждый раз прочитаны `[0, 1]`, после PURGE таблица отсутствует в каталоге |
|
||||
| Raw-ingest | Январь: 2 964 624 строки; январь–март: 9 554 778 строк, 19 колонок |
|
||||
| Профилирование | Схема, null-доли, describe и четыре проверки аномалий выполнены без ошибок |
|
||||
| Самостоятельное задание raw | Lookup: 265 строк; подсчёт зон по Borough и top-5 pickup с названиями зон выполнены; найдено 180 поездок с отрицательной длительностью |
|
||||
| Повторная загрузка raw в отдельном ядре | Все четыре файла пропущены с `skipped_same_size`; снимки всех объектов и каталога до/после совпали |
|
||||
|
||||
Код исходного raw-ingest после ячейки подготовки не изменён, что проверено
|
||||
сравнением ячеек с базовым коммитом. Из подготовки убрано только создание сессии:
|
||||
она теперь приходит из новой практики.
|
||||
|
||||
## Время и ограничения
|
||||
|
||||
- Машинный прогон блока настройки: **179,27 с**. Сюда вошли дополнительные
|
||||
повторы шаблона и таблицы, а также настоящий перезапуск ядра.
|
||||
- Исходная часть модуля, включая заполненные самостоятельные упражнения:
|
||||
**70,41 с**. Полный основной прогон: **250,02 с** (около 4 минут 10 секунд).
|
||||
- Запуск контейнеров и скачивание bundle в замер не входят. Это время выполнения
|
||||
кода; чтение объяснений, самостоятельное заполнение и ответы студента не измерялись.
|
||||
- Ориентир новой практики **20–30 минут**, модуля целиком **около 2 часов**.
|
||||
Общую оценку **12–15 часов** оставили предварительной; машинного замера
|
||||
недостаточно для её уточнения. Нужен самостоятельный проход студента.
|
||||
- Кнопки Jupyter/VS Code, отображение раскрывающегося решения и UI сервисов
|
||||
не проверялись. Перезапуск выполнен менеджером ядра через API, а не кнопкой.
|
||||
- Остальные семь ноутбуков и Trino smoke не выполнялись: runtime-конфигурация
|
||||
не менялась, проверялся модуль 3.
|
||||
|
||||
## Сохранность данных
|
||||
|
||||
До основного прогона в каталоге было **0 таблиц**, в bucket — **4 объекта**.
|
||||
После прогона исходные объекты сохранили ETag и размер, состав каталога
|
||||
не изменился. Это проверка на имевшемся состоянии стенда, а не на копии
|
||||
заполненного студентом курса.
|
||||
|
||||
Добавлены четыре исходных файла Taxi в `raw/nyc_taxi/`, как предусмотрено уроком;
|
||||
они оставлены для модуля 4. Локальный bundle сохранён в игнорируемом `data/`.
|
||||
Файлы данных и метаданных трёх временных таблиц удалены через PURGE.
|
||||
Hadoop S3A оставил шесть нулевых маркеров `data/` и `metadata/`;
|
||||
при завершении проверки удалены только эти шесть зафиксированных объектов.
|
||||
В трёх префиксах практики осталось 0 объектов. Volumes не сбрасывались.
|
||||
Поднятый для проверки стенд остановлен через `docker compose down` без `-v`.
|
||||
|
||||
## Статические проверки
|
||||
|
||||
- `git diff --check`;
|
||||
- `docker compose config --quiet`;
|
||||
- проверка ноутбука через `nbformat.validate` и синтаксиса Python-ячеек через AST;
|
||||
- относительные ссылки изменённых документов и ноутбука;
|
||||
- сохранение восьми модулей и маршрута README → START_HERE → программа → ноутбук.
|
||||
@@ -1,7 +1,11 @@
|
||||
# Модуль 3. Raw-данные и первое чтение в Spark
|
||||
|
||||
**Статус:** `Ready for validation`
|
||||
**Последнее обновление:** `2026-03-07`
|
||||
**Статус:** `Ready for review` — код проверен на стенде, время самостоятельного прохождения студентом пока не измерено
|
||||
**Последнее обновление:** `2026-09-15`
|
||||
|
||||
Дополнение: [#2 — практика настройки SparkSession](https://git.dementev.space/ddmitry/mini-lakehouse-lab/issues/2), после закрытой [#1](https://git.dementev.space/ddmitry/mini-lakehouse-lab/issues/1). Новая практика входит в модуль 3; структура курса остаётся из восьми модулей.
|
||||
|
||||
Результаты прогона и ограничения: [проверка #2](issue-2-spark-session-validation.md).
|
||||
|
||||
## Цель
|
||||
|
||||
@@ -11,6 +15,9 @@
|
||||
|
||||
После прохождения модуля студент:
|
||||
|
||||
- создаёт Spark-сессию для кластера, различает настройки из кода, параметров запуска и общего файла;
|
||||
- проверяет фактический master, имя приложения, настройки каталога и чтение собственной временной Iceberg-таблицы;
|
||||
- объясняет повторное использование контекста, остановку сессии и перезапуск ядра; меняет часовой пояс с восстановлением исходного значения;
|
||||
- понимает, как локальный data bundle попадает в raw-зону MinIO;
|
||||
- умеет загружать PARQUET-файлы в MinIO программно через `boto3`;
|
||||
- умеет читать raw-данные из MinIO через Spark (`spark.read.parquet` по `s3a://` пути);
|
||||
@@ -84,6 +91,20 @@ volumes:
|
||||
### Секция 0: Введение
|
||||
- **[md]** Заголовок, цели модуля, prerequisite (data bundle из `START_HERE.md`). Таблица-сравнение: «Классический DWH: данные уже внутри СУБД» vs «Lakehouse: данные нужно явно доставить в storage».
|
||||
|
||||
### Секция 0.1: Как настроен Spark в нашем стенде
|
||||
|
||||
- **Объяснение:** SparkSession, SparkContext, driver в Jupyter, executors на worker-ах; master, appName, getOrCreate; local и кластер. Источники настроек и подготовленный доступ к Iceberg/MinIO.
|
||||
- **Демонстрация:** сессия с master из общего файла, вычисление суммы 0…9, вывод выбранных несекретных настроек. Изменение `spark.sql.session.timeZone` и восстановление в `finally`.
|
||||
- **Новый запуск:** повторный builder с другим appName сохраняет фактическое имя контекста; после `stop()` новый контекст получает новое имя.
|
||||
- **Самостоятельное повторение:** заполнить адрес кластера, имя приложения и builder; контрольное решение в раскрывающемся блоке. Повторить ячейку и проверить неизменный applicationId.
|
||||
- **Проверка каталога:** создать собственную Iceberg-таблицу с уникальным именем, прочитать `[0, 1]`, удалить только её через `DROP TABLE … PURGE` в `finally`. Учебные таблицы других модулей не используются.
|
||||
- **Checkpoint:** проверить master и appName, объяснить источники настроек и место вычислений. После удаления временной таблицы перезапустить ядро, повторить заполненный шаблон и проверку Iceberg, затем продолжить raw-ingest.
|
||||
- **Длительность:** ориентир 20–30 минут; на модуль целиком около 2 часов. Замер машинного выполнения и ограничения проверки фиксируются отдельно от оценки времени студента.
|
||||
|
||||
### Секция 0.2: Подготовка к raw-ingest
|
||||
|
||||
- Импорты, MinIO-клиент, пути и вспомогательные функции исходного урока. Сессия продолжает работать после checkpoint настройки.
|
||||
|
||||
### Секция 1: Проверка data bundle
|
||||
- **[md]** Зачем проверяем; почему не скачиваем из интернета в ноутбуке (воспроизводимость, сетевые ограничения, разделение ответственности onboarding vs практика).
|
||||
- **[code]** `os.listdir("/opt/data/nyc_taxi")`, вывод файлов с размерами, assert на наличие PARQUET-файлов и `taxi_zone_lookup.csv` — оба нужны для полного прохождения модуля (включая самостоятельное задание в Секции 6). Понятное сообщение с отсылкой к `START_HERE.md` при отсутствии любого из компонентов.
|
||||
@@ -96,7 +117,7 @@ volumes:
|
||||
|
||||
### Секция 3: Первое чтение в Spark
|
||||
- **[md]** Разница `spark.read.parquet("s3a://...")` (raw-файлы) vs `spark.table(...)` (управляемая Iceberg-таблица). Мы пока на этапе raw, Iceberg-таблица появится в Модуле 4.
|
||||
- **[code]** Создание SparkSession (паттерн из Module 2 — адрес кластера из `spark-defaults.conf`, `setLogLevel("ERROR")`).
|
||||
- **[code]** Чтение через сессию, созданную в секции 0.1; повторная инициализация не нужна.
|
||||
- **[code]** Чтение одного файла: `spark.read.parquet("s3a://lakehouse/raw/nyc_taxi/yellow_tripdata_2024-01.parquet")`.
|
||||
- **[code]** Чтение всех файлов wildcard: `yellow_tripdata_*.parquet` (без привязки к конкретному году в wildcard, чтобы код работал с любыми скачанными файлами).
|
||||
|
||||
@@ -130,12 +151,15 @@ volumes:
|
||||
### Дизайн-решения по ноутбуку
|
||||
|
||||
- **Helper-код:** весь код inline в ноутбуке. Отдельный модуль в `src/` не создаётся — студент должен видеть каждый шаг явно (`boto3.upload_file()`, `spark.read.parquet()`, `.printSchema()`).
|
||||
- **Cleanup:** ноутбук НЕ удаляет raw-данные из MinIO — они нужны Модулю 4 (bronze). Spark-сессия останавливается. Явное пояснение в конце ноутбука.
|
||||
- **Cleanup:** практика настройки удаляет только свою временную таблицу. Ноутбук НЕ удаляет raw-данные из MinIO — они нужны Модулю 4 (bronze). Spark-сессия останавливается в конце.
|
||||
|
||||
## Checkpoint
|
||||
|
||||
Студент должен уметь:
|
||||
|
||||
- создать сессию по заполненному шаблону, подтвердить master `spark://spark-master:7077` и заданное appName;
|
||||
- прочитать `[0, 1]` из собственной Iceberg-таблицы и подтвердить её удаление;
|
||||
- объяснить, какие настройки предоставил стенд, где выполняются задания и что повторить после перезапуска ядра;
|
||||
- показать файлы в raw-зоне MinIO (через MinIO Console или через код);
|
||||
- прочитать raw PARQUET через Spark и вывести несколько строк;
|
||||
- назвать схему Yellow Taxi (количество колонок, основные типы);
|
||||
@@ -148,10 +172,11 @@ volumes:
|
||||
- `docker-compose.yml` содержит volume mount `./data:/opt/data:ro` для контейнера `jupyter`, и `docker compose config` проходит без ошибок.
|
||||
- `START_HERE.md` содержит воспроизводимую инструкцию по скачиванию NYC Taxi PARQUET (минимум 3 файла + taxi zone lookup) до запуска Модуля 3.
|
||||
- `.gitignore` содержит `/data/` для исключения датасета из git.
|
||||
- `notebooks/03_raw_ingest_and_first_read.ipynb` можно выполнить сверху вниз в поднятом Jupyter-окружении при наличии скачанного data bundle.
|
||||
- `notebooks/03_raw_ingest_and_first_read.ipynb` можно пройти по порядку в поднятом Jupyter-окружении при наличии data bundle, заполнив шаблон и выполнив указанный перезапуск ядра.
|
||||
- Ноутбук следует методике курса: `объяснение -> демонстрация -> самостоятельное повторение -> checkpoint`.
|
||||
- Ноутбук использует паттерны из Модулей 1-2: `boto3` для MinIO, SparkSession с адресом кластера из `spark-defaults.conf`, `setLogLevel("ERROR")`.
|
||||
- Raw-данные остаются в MinIO после выполнения ноутбука (cleanup отсутствует намеренно).
|
||||
- Демонстрация берёт master из `spark-defaults.conf`; самостоятельное задание явно задаёт тот же адрес, сохраняя подготовленную конфигурацию каталога. Шаблон и контрольное решение проверяются в новых ядрах на Spark 3.5.1.
|
||||
- Проверены повторные ячейки, перезапуск ядра, изменение и восстановление часового пояса, новое имя приложения после остановки контекста.
|
||||
- Raw-данные остаются в MinIO после выполнения ноутбука; удаляется только временная таблица настройки.
|
||||
- При отсутствии data bundle — понятный assert с отсылкой к `START_HERE.md`.
|
||||
- `docs/stack_reference.md` обновлён: секция JupyterLab содержит `./data` mount.
|
||||
- `plans/README.md` содержит строку Модуля 3 в оглавлении.
|
||||
@@ -166,7 +191,8 @@ volumes:
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- Создание Iceberg-таблиц — отложено до Модуля 4.
|
||||
- Создание рабочих Iceberg-таблиц из raw — в Модуле 4. В Модуле 3 есть только маленькая временная таблица для проверки подключения.
|
||||
- Подбор памяти, числа executors, shuffle, GC и эксперимент с числом партиций.
|
||||
- Трансформации и очистка данных — отложено до Модулей 4-5.
|
||||
- Чтение raw-данных через Trino (Trino не умеет читать произвольные файлы без таблицы) — отложено до Модуля 6.
|
||||
- Партиционирование, MERGE, streaming, gold-слой.
|
||||
|
||||
Reference in new issue
Block a user