129 lines
12 KiB
Markdown
129 lines
12 KiB
Markdown
# Прием airports через PXF: материалы автора
|
||
|
||
Лабораторная [#9](https://git.dementev.space/ddmitry/airflow-greenplum/issues/9)
|
||
из [серии #8](https://git.dementev.space/ddmitry/airflow-greenplum/issues/8).
|
||
Студент работает по [инструкции в main](https://git.dementev.space/ddmitry/airflow-greenplum/src/branch/main/docs/assignment/pxf_airports.md).
|
||
Общие принципы программы находятся в [авторской заметке](labs_development.md).
|
||
|
||
## Место и самостоятельная часть
|
||
|
||
Работа идет после быстрого старта и знакомства с моделью, перед существующими
|
||
`ods.airplanes` и `ods.seats`. Входом служит прошедшая эталонная цепочка
|
||
до `dm.sales_report` на учебной `main`; пустые студенческие таблицы не мешают
|
||
начать работу. Полный `bookings_validate` не является условием входа.
|
||
|
||
Выбран `airports`: он небольшой, содержит ключ, многоязычные JSON и координаты,
|
||
а его готовая загрузка ODS дает наблюдаемый результат преобразования.
|
||
Студент удаляет внешний DDL из `sql/stg/airports_ddl.sql` и заменяет
|
||
`sql/stg/airports_load.sql` заглушкой. В базе удаляет внешний объект и все
|
||
снимки только `stg.airports`. Без очистки прошлые строки могли бы скрыть
|
||
отсутствие собственной загрузки.
|
||
|
||
Внутренний DDL, JDBC-конфигурация и DQ остаются готовыми. Добавлен готовый
|
||
`lab_pxf_airports`: восемь PostgresOperator выполняют существующие загрузки
|
||
и проверки четырех справочников. Студент задает
|
||
состав и порядок полей внешней таблицы, `LOCATION`, затем пишет получение
|
||
снимка с ключом и метками. Близкий пример — `airplanes`. Физическая организация
|
||
внутренней таблицы и самостоятельная оркестрация относятся к следующим работам.
|
||
|
||
## Устройство урока
|
||
|
||
Основной текст строится вокруг собственной загрузки и опытов с данными.
|
||
Сначала студент пишет SQL по требованиям и близкому примеру, затем
|
||
предполагает результат повторного выполнения и переименования аэропорта,
|
||
наблюдает данные и объясняет различия. Передача нового названия в ODS
|
||
показывает применение его работы в следующем слое.
|
||
|
||
Команды подготовки, первые действия Airflow, сверка и восстановление
|
||
находятся в раскрываемых блоках рядом с соответствующим действием.
|
||
Проверки остаются готовыми: написание собственной DQ относится к дальнейшим
|
||
заданиям. Результат студента — два SQL-файла и объяснение опытов; собирать
|
||
отчет из JSON, Run ID и логов не требуется.
|
||
|
||
Обозначения A/B/C, проверки намеренно неверной реализации и полный протокол
|
||
приемки относятся к авторским материалам. Они подтверждают пригодность
|
||
лабораторной, но не задают структуру урока. Одного сокращения формулировок
|
||
было бы недостаточно: оно сохранило бы последовательность сбора доказательств
|
||
вместо работы с собственной загрузкой. Демонстрационный разбор также
|
||
не дал бы предусмотренной программой самостоятельной практики.
|
||
|
||
## Полное решение
|
||
|
||
Эталон уже содержится в этой ветке:
|
||
|
||
- [airports_ddl.sql](../../sql/stg/airports_ddl.sql): шесть TEXT-полей,
|
||
`pxf://bookings.airports_data?PROFILE=JDBC&SERVER=bookings-db`,
|
||
`FORMAT 'CUSTOM' (formatter='pxfwritable_import')`.
|
||
- [airports_load.sql](../../sql/stg/airports_load.sql): чтение внешней таблицы,
|
||
`_load_id = '{{ run_id }}'`, две метки `now()`, защита `NOT EXISTS`
|
||
по паре `(_load_id, airport_code)`.
|
||
- [airports_dq.sql](../../sql/stg/airports_dq.sql): базовая обратная связь DAG.
|
||
- [airports_load.sql для ODS](../../sql/ods/airports_load.sql): выбор батча,
|
||
извлечение `ru` и замена текущего снимка.
|
||
|
||
Для воспроизведения автор может после вычитания вернуть два исходных файла
|
||
из сохраненной копии. В учебной ветке они также остаются готовыми при первом
|
||
запуске: вычитание выполняется самим студентом во время работы.
|
||
Скрывать ответ технически не требуется; самостоятельность проверяется
|
||
опытами и объяснением результата.
|
||
|
||
## Почему нужны эти проверки
|
||
|
||
Готовая [сверка в main](https://git.dementev.space/ddmitry/airflow-greenplum/src/branch/main/scripts/check_airports_snapshot.py)
|
||
экспортирует источник напрямую из Postgres, без внешней таблицы студента.
|
||
Поэтому неверный `LOCATION` не становится одновременно ошибкой загрузки
|
||
и ее контрольного источника. В JSON сохраняются все бизнес-поля, а не только
|
||
выбранное русское имя. Перезапись контрольного файла запрещена.
|
||
|
||
В STG проверяются ключи, отсутствие дублей внутри батча, все бизнес-поля,
|
||
`_load_id`, наличие `_load_ts` и `event_ts`. JSON сравнивается по содержимому,
|
||
чтобы порядок свойств не влиял на результат. В ODS проверяется вся таблица,
|
||
русские значения названий и точный STG `_load_id` у каждой строки.
|
||
Штатная DQ сверяет количество и обязательные поля, поэтому сама по себе
|
||
не отвергнет старое непустое название с правильным числом строк.
|
||
|
||
| Случай | Наблюдаемый результат |
|
||
|---|---|
|
||
| Вычитание | STG пуст; внешняя таблица отсутствует; независимая сверка отказывает |
|
||
| A | Все ключи и бизнес-поля совпадают с прямым экспортом источника |
|
||
| Повтор A | Clear только загрузки аэропортов, затем ее DQ в том же запуске; строки сохраняются, генератор не повторяется |
|
||
| Изменение источника | Сохранен исходный JSON одного аэропорта; изменен только `ru` |
|
||
| Неверная старая копия | A или его копия с новым `_load_id` не совпадает с контрольным источником B при равном числе строк |
|
||
| B | B содержит новое имя; A все еще совпадает с исходным экспортом |
|
||
| ODS | Все четыре справочника имеют B; resolver без `conf` выбирает B; ODS содержит B и новое имя |
|
||
| Восстановление C | Источник в точности равен исходному A; новый общий C проходит ODS → DDS → DM |
|
||
|
||
Для повторов A используется Clear отдельных задач. A/B/C создаются
|
||
через `lab_pxf_airports`: повтор снимка не требует нового транзакционного дня.
|
||
Это стало отдельной границей при подготовке опыта: полные запуски STG
|
||
привязывали каждый контроль справочника к длительной генерации Bookings.
|
||
Учебный DAG явно исключает генератор и транзакционные загрузки, но использует
|
||
те же SQL-файлы снимков. Основной `bookings_to_gp_stage` сохраняет свое поведение.
|
||
|
||
Автоматический resolver выбирает последний общий ID по времени готовности.
|
||
Наличие ID во всех таблицах не доказывает их полноту — нужны DQ и сверка.
|
||
Передача `stg_batch_id` через `conf` обошла бы автоматический выбор, поэтому
|
||
в проверочном опыте ODS запускается с пустым `conf`.
|
||
|
||
## Выход и переход
|
||
|
||
В `stg.airports` остаются A/B/C; старые снимки аэропортов удалены в начале.
|
||
В остальных справочниках остаются также снимки быстрого старта. Транзакционные
|
||
таблицы сохраняют данные подготовки. B сохраняет
|
||
учебное изменение как историю; последний общий C и текущий ODS содержат
|
||
восстановленные значения. Источник восстанавливается из постоянной таблицы
|
||
`public.lab9_airport_original`, поэтому закрытие psql не теряет копию.
|
||
Повтор UPDATE восстановления безопасен; таблица удаляется только после сверки.
|
||
|
||
Следующая работа берет общий C для `ods.airplanes` и `ods.seats`, без нового
|
||
STG. Затем сохраняется прежний маршрут измерений и витрин. Работы #10–#14
|
||
остаются отдельными задачами серии; эта лабораторная не добавляет их заглушки
|
||
и не меняет текущие требования к ним.
|
||
|
||
## Проверочный прогон
|
||
|
||
Команды, версии и наблюдения сохраняются в
|
||
[протоколе прогона](../reports/2026-09-22-pxf-airports.md).
|
||
Проверять нужно учебную `main`: полный `solution` заполняет дополнительные
|
||
таблицы и не подтверждает проходимость первой работы с учебными заглушками.
|