docs(practice): уточнить инструкции по результатам прохождения
Уточнены порядок CSV/COPY, расположение логов callbacks, шаблонов и управления TaskGroup. Сохранены условия проверки, результаты 69 контрольных запусков и одного диагностического, SHA-256 DAG и снимки UI. Все этапы плана закрыты. Основной и дополнительный маршруты проверены на Airflow 2.9.2. SMTP проверен локальным приемником, включая отказ доставки; внешняя почта и Windows отдельно не проверялись. Тестовые контейнеры и тома удалены.
This commit is contained in:
1 parent
d8dfe83651
commit
1277bff690
10 files changed
+240
-51
No files matched your search
@@ -1,30 +1,38 @@
|
||||
# План обновления практики по Airflow
|
||||
|
||||
Дата: 2026-09-28. Подход согласован с автором курса. Реализация не начата; продолжение — первый незакрытый пункт в разделе «Порядок работы».
|
||||
Дата: 2026-09-28. Подход согласован с автором курса. Реализация завершена. Результаты и границы проверки сохранены в [протоколе](../validation/2026-09-28-practice.md).
|
||||
|
||||
Этот документ сохраняет решения и порядок дальнейшей работы. Текущие задания находятся в [educational-tasks.md](../../airflow-docker/educational-tasks.md), запуск стенда описан в [README](../../airflow-docker/README.md).
|
||||
Этот документ сохраняет решения, порядок доработки и результаты проверки. Текущие задания находятся в [educational-tasks.md](../../airflow-docker/educational-tasks.md), запуск стенда описан в [README](../../airflow-docker/README.md).
|
||||
|
||||
## Цель и границы
|
||||
|
||||
Стенд знакомит студентов с Apache Airflow и входит в [программу Data Engineering](https://de.dementev.space/). Перед ним студенты проходят Python, pandas, SQL и Docker. Самостоятельное построение цельных процессов обработки данных относится к последующим курсовым работам.
|
||||
|
||||
Результат этого блока: студент умеет запустить готовый DAG, найти его граф и логи, внести небольшое изменение по образцу и объяснить наблюдаемое поведение. Практика остаётся набором небольших экспериментов с инструментом.
|
||||
Результат этого блока: студент умеет запустить готовый DAG, найти его граф и логи, внести небольшое изменение по образцу и объяснить наблюдаемое поведение. Практика остается набором небольших экспериментов с инструментом.
|
||||
|
||||
Обновление сохраняет ознакомительный формат и версию Airflow 2.9.x. Основной маршрут выполняется на существующем стенде; внешние интеграции относятся к заданиям по желанию. Учебная имитация отдельных операций допустима, если описание и логи честно называют её имитацией.
|
||||
Обновление сохраняет ознакомительный формат и версию Airflow 2.9.x. Основной маршрут выполняется на существующем стенде; внешние интеграции относятся к заданиям по желанию. Учебная имитация отдельных операций допустима, если описание и логи честно называют ее имитацией.
|
||||
|
||||
## Порядок работы
|
||||
|
||||
Перед продолжением сверить чек-лист с текущими файлами и историей Git. Прочитать согласованные решения ниже, затем выполнить первый незавершённый этап. Локальные решения в этих границах принимать и записывать в рабочей таблице; противоречия с согласованной целью выносить на обсуждение.
|
||||
Перед продолжением сверить чек-лист с текущими файлами и историей Git. Прочитать согласованные решения ниже, затем выполнить первый незавершенный этап. Локальные решения в этих границах принимать и записывать в рабочей таблице; противоречия с согласованной целью выносить на обсуждение.
|
||||
|
||||
- [x] Зафиксировать подход и находки в этом документе; добавить ссылки из README стенда и AGENTS.md.
|
||||
- [ ] **1. Разобрать все исходные задания.** Создать здесь рабочую таблицу с колонками: исходное задание (DAG/раздел и номер), изучаемая возможность, исходное состояние и зависимости, решение с обоснованием, место в будущем маршруте, наблюдаемый результат. Решения: оставить основным, разделить, перенести в дополнительные или переформулировать. Исходный набор — 25 заданий в `educational-tasks.md` на коммите `13e3102`; после переработки число упражнений может измениться. **Готово:** учтены 25 из 25 исходных заданий, у каждой части разделённого задания определена обязательность, а все зависимости основного маршрута обеспечены без выполнения дополнительных упражнений. Сопоставление с главами учебника объясняет выбор тем; новые темы добавляются только при выявленной необходимости для цели блока.
|
||||
- [ ] **2. Проверить и исправить несогласованности.** Сверить каждую строку раздела «Найденные несогласованности и препятствия» с актуальным кодом. Для каждой записать итог: исправлено, не подтвердилось с объяснением или оставлено как явно описанный учебный сценарий. **Готово:** все находки разобраны; подтверждённые ошибки устранены, изменённое поведение проверено на версии стенда. Невыполненная проверка остаётся открытой.
|
||||
- [ ] **3. Переработать показательный блок обработки ошибок.** Подготовить основной управляемый эксперимент с повтором, наблюдаемые результаты и отдельное усложнение по желанию. **Готово:** исходное состояние и возврат к нему описаны; воспроизведены успех после повтора и окончательная ошибка; логи, состояния задач и итог DAG Run совпадают с описанием. Этот блок служит образцом объёма и оформления остальных упражнений.
|
||||
- [ ] **4. Переработать остальные блоки.** Применить выбранное распределение и образец к остальным заданиям. **Готово:** каждая строка рабочей таблицы ссылается на итоговое упражнение или содержит причину объединения; у каждого упражнения есть обязательность и проверяемый результат; подготовка и восстановление общих данных описаны там, где нужны.
|
||||
- [ ] **5. Согласовать навигацию.** Обновить маршрут и оценку прогресса в `educational-tasks.md`, корневом README и README стенда. Сверить `dag-specifications.md` с итоговым содержанием. **Готово:** ссылки ведут к актуальным упражнениям; документы согласованно описывают порядок и условия завершения; устаревшие прототипы и необоснованные оценки времени удалены или исправлены.
|
||||
- [ ] **6. Пройти маршрут двумя способами.** Выполнить только основную практику, затем дополнительные упражнения с переходами обратно к основной. **Готово:** обе последовательности пройдены в изолированном окружении; указанные результаты подтверждены в данных и UI; сохранены условия запусков, результаты и ограничения по правилам раздела «Как проверять результат доработки».
|
||||
- [x] **1. Разобрать все исходные задания.** Создать здесь рабочую таблицу с колонками: исходное задание (DAG/раздел и номер), изучаемая возможность, исходное состояние и зависимости, решение с обоснованием, место в будущем маршруте, наблюдаемый результат. Решения: оставить основным, разделить, перенести в дополнительные или переформулировать. Исходный набор - 25 заданий в `educational-tasks.md` на коммите `13e3102`; после переработки число упражнений может измениться. **Готово:** учтены 25 из 25 исходных заданий, у каждой части разделенного задания определена обязательность, а все зависимости основного маршрута обеспечены без выполнения дополнительных упражнений. Сопоставление с главами учебника объясняет выбор тем; новые темы добавляются только при выявленной необходимости для цели блока.
|
||||
- [x] **2. Проверить и исправить несогласованности.** Сверить каждую строку раздела "Найденные несогласованности и препятствия" с актуальным кодом. Для каждой записать итог: исправлено, не подтвердилось с объяснением или оставлено как явно описанный учебный сценарий. **Готово:** все находки разобраны; подтвержденные ошибки устранены, измененное поведение проверено на версии стенда. Невыполненная проверка остается открытой.
|
||||
- [x] **3. Переработать показательный блок обработки ошибок.** Подготовить основной управляемый эксперимент с повтором, наблюдаемые результаты и отдельное усложнение по желанию. **Готово:** исходное состояние и возврат к нему описаны; воспроизведены успех после повтора и окончательная ошибка; логи, состояния задач и итог DAG Run совпадают с описанием. Этот блок служит образцом объема и оформления остальных упражнений.
|
||||
- [x] **4. Переработать остальные блоки.** Применить выбранное распределение и образец к остальным заданиям. **Готово:** каждая строка рабочей таблицы ссылается на итоговое упражнение или содержит причину объединения; у каждого упражнения есть обязательность и проверяемый результат; подготовка и восстановление общих данных описаны там, где нужны.
|
||||
- [x] **5. Согласовать навигацию.** Обновить маршрут и оценку прогресса в `educational-tasks.md`, корневом README и README стенда. Сверить `dag-specifications.md` с итоговым содержанием. **Готово:** ссылки ведут к актуальным упражнениям; документы согласованно описывают порядок и условия завершения; устаревшие прототипы и необоснованные оценки времени удалены или исправлены.
|
||||
- [x] **6. Пройти маршрут двумя способами.** Выполнить только основную практику, затем дополнительные упражнения с переходами обратно к основной. **Готово:** обе последовательности пройдены в изолированном окружении; указанные результаты подтверждены в данных и UI; сохранены условия запусков, результаты и ограничения по правилам раздела "Как проверять результат доработки".
|
||||
|
||||
По мере выполнения обновлять рабочую таблицу и чек-лист. У закрытого этапа указывать ссылку на коммит или запись о проверке. Если часть проверок недоступна, записать причину и продолжать независимую работу; соответствующий этап остаётся незавершённым.
|
||||
По мере выполнения обновлять рабочую таблицу и чек-лист. У закрытого этапа указывать ссылку на коммит или запись о проверке. Если часть проверок недоступна, записать причину и продолжать независимую работу; соответствующий этап остается незавершенным.
|
||||
|
||||
## Текущее состояние реализации
|
||||
|
||||
Исправления DAG сохранены в `cf1b770`, задания и навигация в `d8dfe83`. Последующие уточнения и этот протокол входят в завершающий коммит ветки `work/airflow-practice-refresh`.
|
||||
|
||||
Все 25 исходных заданий сопоставлены с 34 упражнениями: 16 основных и 18 по желанию. Основной маршрут пройден без дополнительных упражнений. Затем проверены дополнительные расширения и возвраты к базовой схеме, режимам и настройкам. Сохранены [условия и результаты проверки](../validation/2026-09-28-practice.md), [состояния 70 контрольных и диагностических запусков](../validation/2026-09-28-practice-runs.jsonl) и снимки UI.
|
||||
|
||||
Открытых этапов реализации нет. При следующей правке сверять затронутые задания с кодом и протоколом. Проверка не измеряет трудность на студентах; SMTP проверен локальным приемником, без внешнего почтового провайдера.
|
||||
|
||||
## Согласованные решения
|
||||
|
||||
@@ -32,61 +40,96 @@
|
||||
2. **Ясное начало каждого блока.** Указаны исходный DAG, необходимые файлы, таблицы и настройки, а также изменения, которые остаются от предыдущей работы. Блок определяется учебным смыслом: загрузка CSV и последующая DQ-проверка могут объединять два DAG.
|
||||
3. **Основная практика и задания по желанию.** Обязательность отделена от сложности. Дополнительные задания расположены в конце своего блока; их результаты не нужны для продолжения основной практики. Пропуск дополнительного задания не препятствует завершению блока.
|
||||
4. **Разделение смешанных заданий.** Если одно упражнение объединяет знакомство с возможностью Airflow и значительное усложнение, оно разделяется. Например, TaskGroup и настройка настоящей отправки писем не должны быть одним обязательным заданием.
|
||||
5. **Наблюдаемый результат.** У каждого задания есть раздел «Что должно получиться»: конкретное поведение в UI, сообщение в логах или результат в файле/таблице. Преднамеренная ошибка может быть правильным результатом упражнения.
|
||||
5. **Наблюдаемый результат.** У каждого задания есть раздел "Что должно получиться": конкретное поведение в UI, сообщение в логах или результат в файле/таблице. Преднамеренная ошибка может быть правильным результатом упражнения.
|
||||
6. **Управляемые учебные сценарии.** Основные примеры ветвления и повторных попыток позволяют гарантированно увидеть нужный случай. Случайность допустима в данных, если она не мешает наблюдать изучаемое поведение и проверять результат.
|
||||
7. **Понятное завершение.** Основная практика даёт достаточное знакомство для перехода дальше по программе. Выполнение всех дополнительных упражнений не является скрытым условием прохождения курса.
|
||||
7. **Понятное завершение.** Основная практика дает достаточное знакомство для перехода дальше по программе. Выполнение всех дополнительных упражнений не является скрытым условием прохождения курса.
|
||||
|
||||
Дополнительное задание, меняющее общую таблицу или настройку, должно описывать возврат к состоянию, необходимому следующему блоку. Перенос задания в конец раздела сам по себе эту проблему не решает. Восстановление касается только объектов упражнения, без общего сброса стенда или чужих изменений.
|
||||
|
||||
## Как оформлять задания
|
||||
|
||||
Сохраняется один основной документ для студентов — `educational-tasks.md`. Не нужно дублировать инструкции в отдельном сборнике дополнительных упражнений.
|
||||
Сохраняется один основной документ для студентов - `educational-tasks.md`. Не нужно дублировать инструкции в отдельном сборнике дополнительных упражнений.
|
||||
|
||||
В начале блока: связь с главами учебника, необходимые исходные условия и короткий запуск готового примера. У задания:
|
||||
|
||||
- пометка «Основная практика» или «По желанию»; сложность указывается отдельно;
|
||||
- пометка "Основная практика" или "По желанию"; сложность указывается отдельно;
|
||||
- конкретное изменение, позволяющее познакомиться с одной возможностью Airflow;
|
||||
- подсказка по необходимости;
|
||||
- «Что должно получиться» с указанием места проверки;
|
||||
- для экспериментов с ошибками — способ вызвать ошибку и вернуть нормальное поведение;
|
||||
- "Что должно получиться" с указанием места проверки;
|
||||
- для экспериментов с ошибками - способ вызвать ошибку и вернуть нормальное поведение;
|
||||
- при необходимости один короткий вопрос о причине наблюдаемого результата.
|
||||
|
||||
Для ветвления и обработки ошибок проверяются статусы отдельных задач и всего DAG Run. Формулировка «всё зелёное» не подходит для сценария с ожидаемыми пропусками или ошибками. Проверка результата не должна требовать ожидания недельного расписания или неограниченного числа случайных запусков.
|
||||
Для ветвления и обработки ошибок проверяются статусы отдельных задач и всего DAG Run. Формулировка "всё зеленое" не подходит для сценария с ожидаемыми пропусками или ошибками. Проверка результата не должна требовать ожидания недельного расписания или неограниченного числа случайных запусков.
|
||||
|
||||
## Предварительное распределение содержания
|
||||
## Распределение содержания
|
||||
|
||||
Это начальная гипотеза для этапа 1. Окончательное распределение определяется рабочей таблицей после проверки зависимостей; правила прохождения находятся в разделе «Согласованные решения».
|
||||
Распределение уточнено после разбора 25 исходных заданий. Номера итоговых упражнений указаны в рабочей таблице ниже; правила прохождения находятся в разделе "Согласованные решения".
|
||||
|
||||
| Блок | Основная практика | Возможное усложнение по желанию |
|
||||
|---|---|---|
|
||||
| Первый DAG | Запуск, граф, логи, добавление задачи по образцу, зависимости, знакомство с расписанием | Дополнительные эксперименты с ограничениями запусков |
|
||||
| SQL и Connections | Выполнение SQL, смена подключения, наблюдение результата и ошибки подключения | Дополнительные отчёты и расширение модели данных |
|
||||
| Файлы и преобразования | Проследить путь файла между задачами, изменить простое преобразование или проверку | Новые поля, графики, отчёты, обработка по отделам |
|
||||
| SQL и Connections | Выполнение SQL, смена подключения, наблюдение результата и ошибки подключения | Дополнительные отчеты и расширение модели данных |
|
||||
| Файлы и преобразования | Проследить путь файла между задачами, изменить простое преобразование или проверку | Новые поля, отчеты, обработка по отделам |
|
||||
| CSV → PostgreSQL → DQ | Проследить загрузку, найти путь к файлу в XCom, добавить проверку, увидеть корректные и ошибочные данные | Расширение схемы с согласованным изменением загрузчика и DQ, общая сводка проверок |
|
||||
| Пример ETL | Прочитать граф с параллельными задачами, найти промежуточные и итоговые результаты | Новый источник, расширенная аналитика, архивирование |
|
||||
| Ветвление и Variables | Управляемо выбрать ветку, увидеть `skipped`, понять слияние веток | Третья ветка и полноценная обработка разных форматов |
|
||||
| Ошибки и повторы | Вызвать ошибку, увидеть повтор и успешное завершение, найти причину в логах | Экспоненциальная задержка, разные исключения, эскалация |
|
||||
| Пулы, TaskGroup и оповещения | Короткие отдельные эксперименты: ожидание слотов, группировка существующих задач, сообщение обработчика ошибки | Настоящая отправка писем и более сложные сценарии |
|
||||
| Пулы, XCom и TaskGroup | Ожидание слотов, новая метрика XCom, группировка существующих задач | XCom в Bash-шаблоне; настоящая отправка писем на примере с ошибками |
|
||||
|
||||
Чтение и изменение XCom не должно зависеть от выполнения дополнительного задания по резервному копированию. Для знакомства с пулами достаточно готовых задач с наблюдаемой длительностью; написание механизма резервного копирования не является целью такого эксперимента.
|
||||
|
||||
## Разбор 25 исходных заданий
|
||||
|
||||
Номера слева относятся к версии `13e3102`, номера справа - к новой редакции `educational-tasks.md`. Состав основной практики покрывает темы глав 4-9; главы 1-3 дают контекст перед запуском. Обработка по интервалам и самостоятельный проект остаются следующей ступенью программы.
|
||||
|
||||
| Исходное задание | Возможность | Вход и зависимости | Решение и причина | Новое место | Наблюдаемый результат |
|
||||
|---|---|---|---|---|---|
|
||||
| hello_world / 1 | PythonOperator, зависимости | Исходный DAG | Основное: небольшое изменение графа | [1.1](../../airflow-docker/educational-tasks.md#first-dag) | Новая задача и ее сообщение видны в UI |
|
||||
| hello_world / 2 | Расписание, ограничения запусков | После 1.1 | Разделить: расписание основное, конкуренция по желанию | [1.3, 1.A](../../airflow-docker/educational-tasks.md#first-dag) | Видно расписание; второй запуск ожидает при лимите 1 |
|
||||
| hello_world / 3 | BashOperator | После 1.1 | Основное: другой оператор и файл | [1.2](../../airflow-docker/educational-tasks.md#first-dag) | Файл содержит ожидаемый текст |
|
||||
| sql_basic / 1 | SQL в задаче, схема таблицы | Исходный SQL-DAG и training | Разделить: UPDATE основное, схема по желанию | [2.2, 2.A](../../airflow-docker/educational-tasks.md#sql) | Возраст изменен; дополнительная колонка заполнена |
|
||||
| sql_basic / 2 | PostgresHook и отчет | После 2.2, таблица с тремя строками | По желанию: дополнительная обработка | [2.B](../../airflow-docker/educational-tasks.md#sql) | Файл: 3 студента, средний возраст 23.33 после UPDATE |
|
||||
| sql_basic / 3 | Connections и диагностика | Учебная БД, собственный Conn Id | Основное; вместо неопределенного обработчика - найти и исправить ошибку подключения | [2.1](../../airflow-docker/educational-tasks.md#sql) | Ошибка видна в логах, после исправления запуск успешен |
|
||||
| file_operations / 1 | Генерация, валидация | Исходный файловый DAG | Разделить: простая трансформация основная, новые поля по желанию | [3.1, 3.A](../../airflow-docker/educational-tasks.md#files) | 100 обработанных строк; поля и фильтр проверены отдельно |
|
||||
| file_operations / 2 | Отчет по отделам | Требует 3.A | По желанию; график исключен, поскольку требует отдельной зависимости и не знакомит с новой возможностью Airflow | [3.B](../../airflow-docker/educational-tasks.md#files) | Сумма численности отделов равна числу строк входа |
|
||||
| file_operations / 3 | Параллельность, DQ, retries | Требует 3.A | По желанию; параллельность также видна в основном ETL-примере | [3.C](../../airflow-docker/educational-tasks.md#files) | Раздельные файлы покрывают все отделы, сборка читает их |
|
||||
| csv_to_postgres / 1 | Проведение поля через загрузку | После основной загрузки и DQ | По желанию: согласовать все четыре SQL/CSV-участка и проверку схемы | [4.A](../../airflow-docker/educational-tasks.md#load-dq) | status заполнен; DQ проходит; исходная схема восстановлена |
|
||||
| csv_to_postgres / 2 | Специализированный оператор | Базовая схема orders | По желанию: сравнение двух реализаций уже знакомого действия | [4.B](../../airflow-docker/educational-tasks.md#load-dq) | После замены оператора загрузка и DQ успешны |
|
||||
| csv_to_postgres_dq / 1 | Новая проверка, ошибка данных | Основная загрузка orders | Основное: положительный и отрицательный сценарии | [4.2](../../airflow-docker/educational-tasks.md#load-dq) | Отрицательная сумма вызывает failed; восстановление возвращает success |
|
||||
| csv_to_postgres_dq / 2 | Trigger Rules и итог DAG Run | После 4.2 | По желанию: запуск всех проверок и общая сводка | [4.C](../../airflow-docker/educational-tasks.md#load-dq) | Все проверки завершаются, итог сохраняет ошибку |
|
||||
| data_processing / 1 | Несколько источников | Исходный ETL, свои CSV | По желанию; основной разбор графа добавлен перед расширениями | [5.1, 5.A](../../airflow-docker/educational-tasks.md#etl) | После JOIN остаются 6 заказов и выручка 2105 |
|
||||
| data_processing / 2 | Отчет pandas | После основного запуска, products не нужен | По желанию | [5.B](../../airflow-docker/educational-tasks.md#etl) | Топ заказов и средние сверяются с шестью исходными строками |
|
||||
| data_processing / 3 | Валидация, callback, архив | После 5.1; поля продуктов необязательны | По желанию с конкретной ошибкой и восстановлением | [5.C](../../airflow-docker/educational-tasks.md#etl) | При ошибке архив не создается; после исправления совпадает с результатом |
|
||||
| branching / 1 | Выбор ветки и слияние | Исходный DAG, Variable branch_format | Разделить: две управляемые ветки основные, третья по желанию | [6.1, 6.A](../../airflow-docker/educational-tasks.md#branching) | Выбранная ветка success, другая skipped, слияние success |
|
||||
| branching / 2 | Работа выбранной ветки с данными | Два явно заданных входных файла | По желанию; использовать XCom текущего запуска вместо старых файлов другой ветки | [6.B](../../airflow-docker/educational-tasks.md#branching) | Оба формата дают одинаковые агрегаты в отдельных запусках |
|
||||
| branching / 3 | Variables, неизвестный параметр | После 6.1 | Variable включена в основное; fallback объединен с третьей веткой | [6.1, 6.A](../../airflow-docker/educational-tasks.md#branching) | Неизвестное значение дает понятную ошибку либо review-ветку после расширения |
|
||||
| error_handling / 1 | Повторы и задержка | Исходный DAG, Variable error_mode | Разделить: один управляемый повтор основной, backoff по желанию | [7.1, 7.2, 7.A](../../airflow-docker/educational-tasks.md#errors) | Две попытки; с нулем повторов - failed |
|
||||
| error_handling / 2 | Типы ошибок и оповещение | После 7.2 | По желанию; локальный callback без внешней почты | [7.B](../../airflow-docker/educational-tasks.md#errors) | После исчерпания повторов есть сообщение с task_id и причиной |
|
||||
| error_handling / 3 | logging | После 7.1 | Основное небольшое дополнение вместо механической замены всех print | [7.3](../../airflow-docker/educational-tasks.md#errors) | Собственные сообщения видны в логах |
|
||||
| Дополнительные / 1 | Пулы и слоты | Готовый resource_management_dag, пул из 2 слотов | Основной короткий эксперимент; резервное копирование заменено имитацией чтения | [8.1](../../airflow-docker/educational-tasks.md#resources) | Задачи с весами 2 и 1 выполняются последовательно |
|
||||
| Дополнительные / 2 | XCom и Jinja | После 8.1 | Основная метрика; шаблон Bash по желанию | [8.2, 8.A](../../airflow-docker/educational-tasks.md#resources) | total_rows = 50, шаблон выводит то же значение |
|
||||
| Дополнительные / 3 | TaskGroup и алертинг | После 8.2; для почты нужен SMTP | Разделить: группировка основная, внешняя доставка по желанию | [8.3, 8.B](../../airflow-docker/educational-tasks.md#resources) | Группа раскрывается; XCom работает с полными task_id |
|
||||
|
||||
Основной маршрут сохраняет изменения внутри блоков. Общие данные есть только в паре загрузка/DQ; изменение схемы вынесено после основных опытов, с обратным ALTER и возвратом списков колонок. Задания 3.B и 3.C явно зависят от дополнительного 3.A, но дальнейшая основная практика от этой ветки не зависит. Variables возвращаются к исходным значениям перед переходом дальше.
|
||||
|
||||
## Найденные несогласованности и препятствия
|
||||
|
||||
Ниже зафиксированы результаты чтения кода и заданий. Это не отчёт о полном прохождении стенда: Docker, DAG и UI в рамках обсуждения не запускались. Семантика статусов и зависимостей сверялась с документацией Airflow 2.9.2.
|
||||
Первичные находки подтвердились при чтении кода и выполнении. Ниже их итоговое состояние; подробные результаты приведены в [протоколе](../validation/2026-09-28-practice.md).
|
||||
|
||||
| Место | Наблюдение | Направление исправления |
|
||||
| Место | Исходная проблема | Итог |
|
||||
|---|---|---|
|
||||
| [SQL-задание](../../airflow-docker/educational-tasks.md), [sql_basic_dag.py](../../airflow-docker/dags/sql_basic_dag.py) | Совет заменить завершающий `TRUNCATE` на `DROP` не готовит уже существующую таблицу к вставке в новые колонки: выполнение может упасть раньше завершающей задачи. | Описать подготовку учебной таблицы до запуска и момент проверки данных до очистки. Согласовать имя задачи очистки с её действием. |
|
||||
| [csv_to_postgres.py](../../airflow-docker/dags/csv_to_postgres.py) | Подсказка про добавление `status` упоминает генератор, DDL и COPY, но не итоговый `INSERT … SELECT`. | Провести поле через все этапы и проверять его значения в целевой таблице. |
|
||||
| [csv_to_postgres_dq.py](../../airflow-docker/dags/csv_to_postgres_dq.py) | Строгая проверка схемы ожидает исходные четыре колонки и конфликтует с предыдущим расширением таблицы. | Согласовать порядок упражнений и исходное состояние DQ. Расширение схемы можно выполнять после знакомства с базовой загрузкой и проверками. |
|
||||
| DQ-задание про Trigger Rules | После ошибки при стандартном `all_success` обещан `skipped`, хотя в описанном сценарии ожидается `upstream_failed`. | Исправить ожидаемые состояния и проверить их на стенде. |
|
||||
| [error_handling_dag.py](../../airflow-docker/dags/error_handling_dag.py) | `chain` связывает два списка попарно: обработчик успеха зависит от одной рабочей задачи, обработчик ошибки — от другой. Комментарии описывают общий результат. | Согласовать граф с учебным сценарием; проверить варианты успеха и отказа каждой рабочей задачи, а также итог DAG Run. |
|
||||
| DQ-сводка и [пример алертинга](../../09%20-%20Пулы,%20XCom,%20TaskGroup%20и%20алертинг%20в%20Airflow.md) | В исходном DQ используется `all_success`; риск возникает при предлагаемом в упражнении переходе на `all_done`. Успешная единственная завершающая задача с этим правилом может сделать DAG Run успешным при ошибке внутри. Пример письма также безусловно сообщает об успехе. | Явно определить желаемый итог и проверить согласованность статусов с сообщением. Отправку уведомления и успешность обработки рассматривать отдельно. |
|
||||
| [data_processing_dag.py](../../airflow-docker/dags/data_processing_dag.py) | Загрузка в БД имитируется, но лог сообщает о загрузке. Transform читает исходные файлы, а не результаты Extract. | Честно назвать имитацию и согласовать показанный путь данных с фактическими входами задач. Полноценную загрузку ради этого примера добавлять необязательно. |
|
||||
| Задание про пулы | Для пула из двух слотов и двух задач с потреблением два и один предлагается наблюдать «не более двух задач». Это не показывает существенное ограничение: эти две задачи одновременно не помещаются. | Описать ожидаемое ожидание одной задачи и дать достаточную длительность для наблюдения в UI. |
|
||||
| Ветвление, повторы и дополнительные упражнения | Случайный выбор мешает гарантированно наблюдать нужный случай. Сравнение файлов двух взаимоисключающих веток не определяет, от каких запусков брать результаты. Некоторые задания требуют полей, добавленных раньше. | Задать управляемые сценарии, понятные входы и зависимости; исключить случайное использование результатов старого запуска. |
|
||||
| SQL | DROP после вставки не готовил существующую таблицу к новой колонке; результат очищался до проверки. | Исправлено: TRUNCATE перед вставкой, task_id `truncate_table`, три строки остаются в БД. В 2.A ALTER выполняется до запуска. Проверены UPDATE, email и отчет. |
|
||||
| Загрузчик CSV | Подсказка про status пропускала INSERT ... SELECT. | Исправлено: перечислены четыре участка загрузчика и порядок полей CSV/COPY. Проверены значения статусов в БД, а не только наличие колонки. |
|
||||
| Схема DQ | Дополнительная колонка конфликтовала с ожидаемыми четырьмя. | Исправлено: расширение идет после основного DQ; описаны обновление ожидания и возврат схемы. Оба состояния проверены. |
|
||||
| Trigger Rules DQ | После отказа обещан skipped. | Исправлено на upstream_failed; подтверждены отказ проверки суммы и отказ схемы. |
|
||||
| Обработчики ошибок | chain связывал списки попарно. | Исправлено: оба обработчика имеют обе рабочие задачи в непосредственных предшественниках. Проверены успех, повтор, отказ первой, второй и обеих задач, общий итог DAG Run. |
|
||||
| Сводка DQ и алертинг | Единственная успешная завершающая задача с all_done могла скрыть ошибку; письмо безусловно сообщало об успехе. | Исправлено: сводка сверяет состояния и падает при неуспешной проверке. Уведомления назначены callbacks задачи и DAG. Проверены all_done, письма об отказе/успехе и сохранение результата при ошибке SMTP. |
|
||||
| ETL | Transform обходил результаты Extract, имитация называлась загрузкой в БД. | Исправлен путь данных. Имитация сохранена и явно обозначена именем simulate_load и логом. Фильтрация Extract изменила отчет с 6/2105 на 4/2000. |
|
||||
| Пулы | Формулировка "не более двух задач" не показывала ограничение по весам. | Добавлен готовый пример: в пуле из двух слотов веса 2+1 выполняются последовательно, 1+1 пересекаются по времени. Проверены оба варианта. |
|
||||
| Ветвление, повторы и зависимости | Случайность мешала наблюдать нужный случай; использовались неопределенные входы и результаты старых запусков. | Исправлено: Variables управляют режимами; входы CSV/JSON заданы; слияние читает XCom текущего запуска; зависимости дополнительных упражнений указаны явно. |
|
||||
| Логи callbacks | Первоначальная инструкция отправляла все сообщения в docker compose logs scheduler. | Уточнено после выполнения: callback оператора виден в последней попытке задачи, callback DAG в файле scheduler. Оба места проверены. |
|
||||
|
||||
Источники для технической сверки: [chain](https://airflow.apache.org/docs/apache-airflow/2.9.2/_api/airflow/models/baseoperator/index.html#airflow.models.baseoperator.chain), [статусы задач](https://airflow.apache.org/docs/apache-airflow/2.9.2/core-concepts/tasks.html#task-instances), [итог DAG Run](https://airflow.apache.org/docs/apache-airflow/2.9.2/core-concepts/dag-run.html#dag-run-status), [пулы](https://airflow.apache.org/docs/apache-airflow/2.9.2/administration-and-deployment/pools.html).
|
||||
Технические источники: [chain](https://airflow.apache.org/docs/apache-airflow/2.9.2/_api/airflow/models/baseoperator/index.html#airflow.models.baseoperator.chain), [статусы задач](https://airflow.apache.org/docs/apache-airflow/2.9.2/core-concepts/tasks.html#task-instances), [итог DAG Run](https://airflow.apache.org/docs/apache-airflow/2.9.2/core-concepts/dag-run.html#dag-run-status), [пулы](https://airflow.apache.org/docs/apache-airflow/2.9.2/administration-and-deployment/pools.html), [COPY в PostgreSQL 16](https://www.postgresql.org/docs/16/sql-copy.html). Условия поиска логов уточнены по фактическому стенду 2.9.2.
|
||||
|
||||
## Как проверять результат доработки
|
||||
|
||||
@@ -97,7 +140,7 @@
|
||||
- **Документы и исходники:** все 25 исходных заданий учтены, ссылки работают, имена файлов/задач/таблиц совпадают, обязательность и зависимости согласованы.
|
||||
- **Стенд:** подтверждены версия Airflow и загрузка DAG без ошибок импорта; выполнены затронутые сценарии и проверены файлы/таблицы. Проверка синтаксиса Python не заменяет выполнение задач.
|
||||
- **UI:** студент действительно может найти указанные состояния, логи, XCom, граф и настройки; показанные результаты совпадают с текстом.
|
||||
- **Прохождение:** отдельно проверены основной маршрут и переходы после дополнительных заданий. Не выполненные проверки так и отмечаются; предполагаемое время не выдаётся за измеренное на студентах.
|
||||
- **Прохождение:** отдельно проверены основной маршрут и переходы после дополнительных заданий. Не выполненные проверки так и отмечаются; предполагаемое время не выдается за измеренное на студентах.
|
||||
|
||||
Эксперименты с изменением данных выполняются в изолированном учебном окружении. Проверка не должна затрагивать другие работающие стенды или несвязанные изменения пользователя.
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
{"type": "environment", "date": "2026-09-28", "airflow": "2.9.2", "python": "3.12.4", "executor": "LocalExecutor", "required_scenarios": 69, "recorded_runs": 70, "final_source_sha256": {"branching_dag.py": "873f00e26ede268582245aac0ec3b82f858a3654fb7a4e1e08eb6e68ec5d031a", "csv_to_postgres.py": "43452b5f639365be07174e417fa981116b478e90acaeffb184e530d9ae67cb3b", "csv_to_postgres_dq.py": "05e43bc4bdab64813d7a853fee9285c11cb0e82a077c33a641219ca47b4604af", "data_processing_dag.py": "df39a91f89ff2e572d0a101b52171b9af97fb4abc7a66fc90c136425acb64b89", "error_handling_dag.py": "914e00b5845afa82c08aa540501a9994825b9b48a375bcc65ec59ae87b2cc23f", "file_operations_dag.py": "c4b5969a296af94c61cd63a9511a5572f9b858cbd75be913e8dffd015441b48f", "hello_world_dag.py": "09036daee495267a9dc23fd9c04fbd99c50b76194be1f3004822cee87f7797e7", "resource_management_dag.py": "03774dbfe5cadc6d6f036814f4fb2f8db0991c511b7ea3e877f577dca644b13e", "sql_basic_dag.py": "d028ff3d281ad322e8443447f43000497db67053cfeb757dc23b21b78f8b536f"}}
|
||||
{"label": "base_branching_dag", "dag": "branching_dag", "run_id": "base_branching_dag__1790598303491558368", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:25:04.466969+00:00", "end": "2026-09-28T12:25:04.466989+00:00"}, "choose_format": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:06.300063+00:00", "end": "2026-09-28T12:25:07.740076+00:00"}, "process_csv_branch": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:09.192486+00:00", "end": "2026-09-28T12:25:09.736934+00:00"}, "end_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:25:11.926795+00:00", "end": "2026-09-28T12:25:11.926806+00:00"}, "process_json_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:25:07.614511+00:00", "end": "2026-09-28T12:25:07.614511+00:00"}, "merge_results": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:10.695251+00:00", "end": "2026-09-28T12:25:11.211111+00:00"}}}
|
||||
{"label": "base_csv_to_postgres", "dag": "csv_to_postgres", "run_id": "base_csv_to_postgres__1790598316856917563", "state": "success", "expected_state": "success", "tasks": {"create_orders_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:17.703222+00:00", "end": "2026-09-28T12:25:18.385307+00:00"}, "generate_csv": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:19.297092+00:00", "end": "2026-09-28T12:25:20.954830+00:00"}, "preview_csv": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:21.691404+00:00", "end": "2026-09-28T12:25:23.672198+00:00"}, "load_csv_to_postgres": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:24.349364+00:00", "end": "2026-09-28T12:25:25.132534+00:00"}}}
|
||||
{"label": "base_data_processing_dag", "dag": "data_processing_dag", "run_id": "base_data_processing_dag__1790598297048939564", "state": "success", "expected_state": "success", "tasks": {"create_sample_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:24:58.018194+00:00", "end": "2026-09-28T12:24:59.516887+00:00"}, "extract_orders": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:00.524479+00:00", "end": "2026-09-28T12:25:04.438975+00:00"}, "extract_customers": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:00.319521+00:00", "end": "2026-09-28T12:25:04.581621+00:00"}, "transform_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:06.641381+00:00", "end": "2026-09-28T12:25:08.648080+00:00"}, "simulate_load": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:10.564728+00:00", "end": "2026-09-28T12:25:11.970738+00:00"}, "generate_report": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:13.716924+00:00", "end": "2026-09-28T12:25:15.365893+00:00"}}}
|
||||
{"label": "base_dq", "dag": "csv_to_postgres_dq", "run_id": "base_dq__1790598363500865461", "state": "success", "expected_state": "success", "tasks": {"data_quality_summary": {"state": "success", "attempts": 1, "start": "2026-09-28T12:26:09.667195+00:00", "end": "2026-09-28T12:26:10.149648+00:00"}, "check_orders_schema": {"state": "success", "attempts": 1, "start": "2026-09-28T12:26:05.724275+00:00", "end": "2026-09-28T12:26:06.206894+00:00"}, "check_orders_has_rows": {"state": "success", "attempts": 1, "start": "2026-09-28T12:26:06.888967+00:00", "end": "2026-09-28T12:26:07.337686+00:00"}, "check_order_duplicates": {"state": "success", "attempts": 1, "start": "2026-09-28T12:26:08.530504+00:00", "end": "2026-09-28T12:26:09.010861+00:00"}, "check_orders_table_exists": {"state": "success", "attempts": 1, "start": "2026-09-28T12:26:04.557178+00:00", "end": "2026-09-28T12:26:05.087473+00:00"}}}
|
||||
{"label": "base_error_handling_dag", "dag": "error_handling_dag", "run_id": "base_error_handling_dag__1790598307068800147", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:25:08.688153+00:00", "end": "2026-09-28T12:25:08.688162+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:09.327897+00:00", "end": "2026-09-28T12:25:09.842635+00:00"}, "success_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:21.859653+00:00", "end": "2026-09-28T12:25:22.425907+00:00"}, "retry_task": {"state": "success", "attempts": 2, "start": "2026-09-28T12:25:20.608453+00:00", "end": "2026-09-28T12:25:21.211328+00:00"}, "failure_handler": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:25:21.289459+00:00", "end": "2026-09-28T12:25:21.289459+00:00"}}}
|
||||
{"label": "base_file_operations_dag", "dag": "file_operations_dag", "run_id": "base_file_operations_dag__1790598285829374474", "state": "success", "expected_state": "success", "tasks": {"generate_sample_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:24:47.788802+00:00", "end": "2026-09-28T12:24:52.901884+00:00"}, "read_and_validate_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:24:54.105194+00:00", "end": "2026-09-28T12:24:56.336898+00:00"}, "transform_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:24:57.980925+00:00", "end": "2026-09-28T12:24:59.453129+00:00"}, "write_summary": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:00.578118+00:00", "end": "2026-09-28T12:25:05.022890+00:00"}}}
|
||||
{"label": "base_hello_world_dag", "dag": "hello_world_dag", "run_id": "base_hello_world_dag__1790598285331279953", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:24:47.450226+00:00", "end": "2026-09-28T12:24:48.038258+00:00"}, "date_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:24:50.252850+00:00", "end": "2026-09-28T12:24:50.964908+00:00"}, "end_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:24:54.481818+00:00", "end": "2026-09-28T12:24:55.586281+00:00"}}}
|
||||
{"label": "base_resource_management_dag", "dag": "resource_management_dag", "run_id": "base_resource_management_dag__1790598315287681546", "state": "success", "expected_state": "success", "tasks": {"read_customers": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:16.416179+00:00", "end": "2026-09-28T12:25:36.854962+00:00"}, "read_orders": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:38.182441+00:00", "end": "2026-09-28T12:25:58.742686+00:00"}, "calculate_metrics": {"state": "success", "attempts": 1, "start": "2026-09-28T12:25:59.666739+00:00", "end": "2026-09-28T12:26:00.315747+00:00"}, "log_metrics": {"state": "success", "attempts": 1, "start": "2026-09-28T12:26:00.826752+00:00", "end": "2026-09-28T12:26:01.327392+00:00"}}}
|
||||
{"label": "base_sql_basic_dag", "dag": "sql_basic_dag", "run_id": "base_sql_basic_dag__1790598285733066584", "state": "success", "expected_state": "success", "tasks": {"create_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:24:47.558668+00:00", "end": "2026-09-28T12:24:48.171774+00:00"}, "truncate_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:24:50.270342+00:00", "end": "2026-09-28T12:24:50.925384+00:00"}, "insert_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:24:54.165693+00:00", "end": "2026-09-28T12:24:55.422059+00:00"}, "query_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:24:58.018381+00:00", "end": "2026-09-28T12:24:58.580976+00:00"}}}
|
||||
{"label": "main_1_1", "dag": "hello_world_dag", "run_id": "main_1_1__1790598669894851732", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:31:11.829375+00:00", "end": "2026-09-28T12:31:12.443251+00:00"}, "weather_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:31:12.949206+00:00", "end": "2026-09-28T12:31:13.415511+00:00"}, "date_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:31:14.385576+00:00", "end": "2026-09-28T12:31:14.885931+00:00"}, "end_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:31:16.391743+00:00", "end": "2026-09-28T12:31:16.910520+00:00"}}}
|
||||
{"label": "main_1_2_3", "dag": "hello_world_dag", "run_id": "main_1_2_3__1790598718302127877", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:31:59.745450+00:00", "end": "2026-09-28T12:32:00.316366+00:00"}, "write_note": {"state": "success", "attempts": 1, "start": "2026-09-28T12:32:05.072071+00:00", "end": "2026-09-28T12:32:06.021474+00:00"}, "weather_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:32:01.159334+00:00", "end": "2026-09-28T12:32:01.723974+00:00"}, "date_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:32:02.837180+00:00", "end": "2026-09-28T12:32:03.474962+00:00"}, "end_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:32:07.286250+00:00", "end": "2026-09-28T12:32:07.807426+00:00"}}}
|
||||
{"label": "main_2_1_bad_connection", "dag": "sql_basic_dag", "run_id": "main_2_1_bad_connection__1790598823284321738", "state": "failed", "expected_state": "failed", "tasks": {"truncate_table": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:33:44.966461+00:00", "end": "2026-09-28T12:33:44.966461+00:00"}, "insert_data": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:33:45.092205+00:00", "end": "2026-09-28T12:33:45.092205+00:00"}, "query_data": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:33:46.184509+00:00", "end": "2026-09-28T12:33:46.184509+00:00"}, "create_table": {"state": "failed", "attempts": 1, "start": "2026-09-28T12:33:44.394182+00:00", "end": "2026-09-28T12:33:44.864384+00:00"}}}
|
||||
{"label": "main_2_1_connection", "dag": "sql_basic_dag", "run_id": "main_2_1_connection__1790598770237278526", "state": "success", "expected_state": "success", "tasks": {"truncate_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:32:53.727892+00:00", "end": "2026-09-28T12:32:54.590903+00:00"}, "query_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:32:57.779131+00:00", "end": "2026-09-28T12:32:58.430291+00:00"}, "insert_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:32:55.515062+00:00", "end": "2026-09-28T12:32:56.385369+00:00"}, "create_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:32:51.379822+00:00", "end": "2026-09-28T12:32:52.074251+00:00"}}}
|
||||
{"label": "main_2_2_repeat", "dag": "sql_basic_dag", "run_id": "main_2_2_repeat__1790598880752727153", "state": "success", "expected_state": "success", "tasks": {"create_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:34:42.273136+00:00", "end": "2026-09-28T12:34:42.874104+00:00"}, "truncate_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:34:43.459390+00:00", "end": "2026-09-28T12:34:44.136238+00:00"}, "insert_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:34:45.510497+00:00", "end": "2026-09-28T12:34:46.049469+00:00"}, "query_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:34:49.918420+00:00", "end": "2026-09-28T12:34:50.754474+00:00"}, "update_student": {"state": "success", "attempts": 1, "start": "2026-09-28T12:34:47.643123+00:00", "end": "2026-09-28T12:34:48.453621+00:00"}}}
|
||||
{"label": "main_2_2_update", "dag": "sql_basic_dag", "run_id": "main_2_2_update__1790598869163909877", "state": "success", "expected_state": "success", "tasks": {"create_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:34:30.191396+00:00", "end": "2026-09-28T12:34:30.709128+00:00"}, "truncate_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:34:31.374968+00:00", "end": "2026-09-28T12:34:31.950460+00:00"}, "insert_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:34:33.266504+00:00", "end": "2026-09-28T12:34:33.826087+00:00"}, "update_student": {"state": "success", "attempts": 1, "start": "2026-09-28T12:34:34.392167+00:00", "end": "2026-09-28T12:34:34.997350+00:00"}, "query_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:34:35.712919+00:00", "end": "2026-09-28T12:34:36.267889+00:00"}}}
|
||||
{"label": "main_3_1", "dag": "file_operations_dag", "run_id": "main_3_1__1790598934667802974", "state": "success", "expected_state": "success", "tasks": {"transform_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:35:41.591826+00:00", "end": "2026-09-28T12:35:43.072378+00:00"}, "write_summary": {"state": "success", "attempts": 1, "start": "2026-09-28T12:35:43.657051+00:00", "end": "2026-09-28T12:35:45.282296+00:00"}, "read_and_validate_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:35:38.756892+00:00", "end": "2026-09-28T12:35:40.407604+00:00"}, "generate_sample_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:35:35.850235+00:00", "end": "2026-09-28T12:35:37.611113+00:00"}}}
|
||||
{"label": "main_4_1", "dag": "csv_to_postgres", "run_id": "main_4_1__1790598948261221421", "state": "success", "expected_state": "success", "tasks": {"preview_csv": {"state": "success", "attempts": 1, "start": "2026-09-28T12:35:54.292219+00:00", "end": "2026-09-28T12:35:56.183442+00:00"}, "load_csv_to_postgres": {"state": "success", "attempts": 1, "start": "2026-09-28T12:35:57.028083+00:00", "end": "2026-09-28T12:35:57.557853+00:00"}, "create_orders_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:35:49.970156+00:00", "end": "2026-09-28T12:35:50.510862+00:00"}, "generate_csv": {"state": "success", "attempts": 1, "start": "2026-09-28T12:35:51.118676+00:00", "end": "2026-09-28T12:35:52.724944+00:00"}}}
|
||||
{"label": "main_4_2_negative", "dag": "csv_to_postgres_dq", "run_id": "main_4_2_negative__1790599011734924894", "state": "failed", "expected_state": "failed", "tasks": {"check_orders_table_exists": {"state": "success", "attempts": 1, "start": "2026-09-28T12:36:52.890470+00:00", "end": "2026-09-28T12:36:53.470813+00:00"}, "check_orders_schema": {"state": "success", "attempts": 1, "start": "2026-09-28T12:36:54.443034+00:00", "end": "2026-09-28T12:36:54.969375+00:00"}, "check_positive_amount": {"state": "failed", "attempts": 1, "start": "2026-09-28T12:36:59.434123+00:00", "end": "2026-09-28T12:37:00.000016+00:00"}, "check_orders_has_rows": {"state": "success", "attempts": 1, "start": "2026-09-28T12:36:56.309978+00:00", "end": "2026-09-28T12:36:56.841855+00:00"}, "data_quality_summary": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:37:00.114864+00:00", "end": "2026-09-28T12:37:00.114864+00:00"}, "check_order_duplicates": {"state": "success", "attempts": 1, "start": "2026-09-28T12:36:57.649020+00:00", "end": "2026-09-28T12:36:58.151617+00:00"}}}
|
||||
{"label": "main_4_2_positive", "dag": "csv_to_postgres_dq", "run_id": "main_4_2_positive__1790599000150640710", "state": "success", "expected_state": "success", "tasks": {"check_positive_amount": {"state": "success", "attempts": 1, "start": "2026-09-28T12:36:46.773434+00:00", "end": "2026-09-28T12:36:47.237395+00:00"}, "data_quality_summary": {"state": "success", "attempts": 1, "start": "2026-09-28T12:36:47.780290+00:00", "end": "2026-09-28T12:36:48.296235+00:00"}, "check_orders_table_exists": {"state": "success", "attempts": 1, "start": "2026-09-28T12:36:41.328500+00:00", "end": "2026-09-28T12:36:41.826903+00:00"}, "check_orders_schema": {"state": "success", "attempts": 1, "start": "2026-09-28T12:36:42.632046+00:00", "end": "2026-09-28T12:36:43.225919+00:00"}, "check_orders_has_rows": {"state": "success", "attempts": 1, "start": "2026-09-28T12:36:44.252071+00:00", "end": "2026-09-28T12:36:44.799840+00:00"}, "check_order_duplicates": {"state": "success", "attempts": 1, "start": "2026-09-28T12:36:45.510631+00:00", "end": "2026-09-28T12:36:46.128483+00:00"}}}
|
||||
{"label": "main_4_2_restore", "dag": "csv_to_postgres_dq", "run_id": "main_4_2_restore__1790599023379510146", "state": "success", "expected_state": "success", "tasks": {"check_orders_table_exists": {"state": "success", "attempts": 1, "start": "2026-09-28T12:37:04.600783+00:00", "end": "2026-09-28T12:37:05.244958+00:00"}, "check_orders_schema": {"state": "success", "attempts": 1, "start": "2026-09-28T12:37:06.720511+00:00", "end": "2026-09-28T12:37:07.273312+00:00"}, "check_order_duplicates": {"state": "success", "attempts": 1, "start": "2026-09-28T12:37:09.578662+00:00", "end": "2026-09-28T12:37:10.260325+00:00"}, "check_orders_has_rows": {"state": "success", "attempts": 1, "start": "2026-09-28T12:37:07.872450+00:00", "end": "2026-09-28T12:37:08.452528+00:00"}, "data_quality_summary": {"state": "success", "attempts": 1, "start": "2026-09-28T12:37:12.613421+00:00", "end": "2026-09-28T12:37:13.154513+00:00"}, "check_positive_amount": {"state": "success", "attempts": 1, "start": "2026-09-28T12:37:11.411627+00:00", "end": "2026-09-28T12:37:11.924165+00:00"}}}
|
||||
{"label": "main_5_1_filter", "dag": "data_processing_dag", "run_id": "main_5_1_filter__1790599074663133799", "state": "success", "expected_state": "success", "tasks": {"create_sample_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:37:55.824116+00:00", "end": "2026-09-28T12:37:57.680612+00:00"}, "extract_customers": {"state": "success", "attempts": 1, "start": "2026-09-28T12:37:59.047390+00:00", "end": "2026-09-28T12:38:00.783528+00:00"}, "extract_orders": {"state": "success", "attempts": 1, "start": "2026-09-28T12:37:59.039283+00:00", "end": "2026-09-28T12:38:00.812189+00:00"}, "transform_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:38:01.501036+00:00", "end": "2026-09-28T12:38:03.122143+00:00"}, "simulate_load": {"state": "success", "attempts": 1, "start": "2026-09-28T12:38:04.173110+00:00", "end": "2026-09-28T12:38:05.600297+00:00"}, "generate_report": {"state": "success", "attempts": 1, "start": "2026-09-28T12:38:06.618522+00:00", "end": "2026-09-28T12:38:08.794823+00:00"}}}
|
||||
{"label": "main_5_1_restore", "dag": "data_processing_dag", "run_id": "main_5_1_restore__1790599131072124593", "state": "success", "expected_state": "success", "tasks": {"create_sample_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:38:52.059583+00:00", "end": "2026-09-28T12:38:53.524667+00:00"}, "extract_customers": {"state": "success", "attempts": 1, "start": "2026-09-28T12:38:55.185088+00:00", "end": "2026-09-28T12:38:56.827156+00:00"}, "transform_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:38:58.100743+00:00", "end": "2026-09-28T12:38:59.670338+00:00"}, "simulate_load": {"state": "success", "attempts": 1, "start": "2026-09-28T12:39:01.276000+00:00", "end": "2026-09-28T12:39:02.673380+00:00"}, "extract_orders": {"state": "success", "attempts": 1, "start": "2026-09-28T12:38:55.342213+00:00", "end": "2026-09-28T12:38:56.915006+00:00"}, "generate_report": {"state": "success", "attempts": 1, "start": "2026-09-28T12:39:03.515006+00:00", "end": "2026-09-28T12:39:04.916556+00:00"}}}
|
||||
{"label": "main_6_1_csv", "dag": "branching_dag", "run_id": "main_6_1_csv__1790599221866037738", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:40:23.121466+00:00", "end": "2026-09-28T12:40:23.121476+00:00"}, "process_csv_branch": {"state": "success", "attempts": 1, "start": "2026-09-28T12:40:25.928269+00:00", "end": "2026-09-28T12:40:26.732622+00:00"}, "process_json_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:40:25.140559+00:00", "end": "2026-09-28T12:40:25.140559+00:00"}, "choose_format": {"state": "success", "attempts": 1, "start": "2026-09-28T12:40:24.652209+00:00", "end": "2026-09-28T12:40:25.245620+00:00"}, "merge_results": {"state": "success", "attempts": 1, "start": "2026-09-28T12:40:27.779111+00:00", "end": "2026-09-28T12:40:28.278924+00:00"}, "end_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:40:28.547843+00:00", "end": "2026-09-28T12:40:28.547856+00:00"}}}
|
||||
{"label": "main_6_1_csv_restore", "dag": "branching_dag", "run_id": "main_6_1_csv_restore__1790599246736022049", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:40:47.688841+00:00", "end": "2026-09-28T12:40:47.688856+00:00"}, "process_json_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:40:49.804302+00:00", "end": "2026-09-28T12:40:49.804302+00:00"}, "choose_format": {"state": "success", "attempts": 1, "start": "2026-09-28T12:40:49.301493+00:00", "end": "2026-09-28T12:40:49.954481+00:00"}, "process_csv_branch": {"state": "success", "attempts": 1, "start": "2026-09-28T12:40:50.449791+00:00", "end": "2026-09-28T12:40:51.052888+00:00"}, "merge_results": {"state": "success", "attempts": 1, "start": "2026-09-28T12:40:51.831887+00:00", "end": "2026-09-28T12:40:52.503932+00:00"}, "end_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:40:52.882378+00:00", "end": "2026-09-28T12:40:52.882395+00:00"}}}
|
||||
{"label": "main_6_1_json", "dag": "branching_dag", "run_id": "main_6_1_json__1790599230837355984", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:40:31.315989+00:00", "end": "2026-09-28T12:40:31.315999+00:00"}, "process_csv_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:40:33.528600+00:00", "end": "2026-09-28T12:40:33.528600+00:00"}, "choose_format": {"state": "success", "attempts": 1, "start": "2026-09-28T12:40:33.004290+00:00", "end": "2026-09-28T12:40:33.646275+00:00"}, "process_json_branch": {"state": "success", "attempts": 1, "start": "2026-09-28T12:40:34.836572+00:00", "end": "2026-09-28T12:40:35.404802+00:00"}, "merge_results": {"state": "success", "attempts": 1, "start": "2026-09-28T12:40:36.209056+00:00", "end": "2026-09-28T12:40:36.773333+00:00"}, "end_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:40:37.541270+00:00", "end": "2026-09-28T12:40:37.541279+00:00"}}}
|
||||
{"label": "main_6_1_xml", "dag": "branching_dag", "run_id": "main_6_1_xml__1790599239960170363", "state": "failed", "expected_state": "failed", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:40:40.887013+00:00", "end": "2026-09-28T12:40:40.887026+00:00"}, "choose_format": {"state": "failed", "attempts": 1, "start": "2026-09-28T12:40:42.076249+00:00", "end": "2026-09-28T12:40:42.682151+00:00"}, "process_csv_branch": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:40:42.871931+00:00", "end": "2026-09-28T12:40:42.871931+00:00"}, "process_json_branch": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:40:42.884065+00:00", "end": "2026-09-28T12:40:42.884065+00:00"}, "merge_results": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:40:43.830677+00:00", "end": "2026-09-28T12:40:43.830677+00:00"}, "end_task": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:40:44.428621+00:00", "end": "2026-09-28T12:40:44.428621+00:00"}}}
|
||||
{"label": "main_7_1", "dag": "error_handling_dag", "run_id": "main_7_1__1790599256282469073", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:40:56.875385+00:00", "end": "2026-09-28T12:40:56.875397+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:40:58.566416+00:00", "end": "2026-09-28T12:40:59.270822+00:00"}, "retry_task": {"state": "success", "attempts": 2, "start": "2026-09-28T12:41:09.795211+00:00", "end": "2026-09-28T12:41:10.316195+00:00"}, "failure_handler": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:41:10.449363+00:00", "end": "2026-09-28T12:41:10.449363+00:00"}, "success_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:41:11.113518+00:00", "end": "2026-09-28T12:41:11.598224+00:00"}}}
|
||||
{"label": "main_7_2_fail_both", "dag": "error_handling_dag", "run_id": "main_7_2_fail_both__1790599401713196879", "state": "failed", "expected_state": "failed", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:43:22.530430+00:00", "end": "2026-09-28T12:43:22.530442+00:00"}, "retry_task": {"state": "failed", "attempts": 2, "start": "2026-09-28T12:43:36.132875+00:00", "end": "2026-09-28T12:43:36.780473+00:00"}, "unreliable_task": {"state": "failed", "attempts": 2, "start": "2026-09-28T12:43:36.118123+00:00", "end": "2026-09-28T12:43:36.741330+00:00"}, "success_handler": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:43:36.841371+00:00", "end": "2026-09-28T12:43:36.841371+00:00"}, "failure_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:43:37.573043+00:00", "end": "2026-09-28T12:43:38.121760+00:00"}}}
|
||||
{"label": "main_7_2_fail_first", "dag": "error_handling_dag", "run_id": "main_7_2_fail_first__1790599364116452900", "state": "failed", "expected_state": "failed", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:42:44.750400+00:00", "end": "2026-09-28T12:42:44.750408+00:00"}, "retry_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:42:45.926971+00:00", "end": "2026-09-28T12:42:46.477915+00:00"}, "failure_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:42:59.251667+00:00", "end": "2026-09-28T12:42:59.716135+00:00"}, "unreliable_task": {"state": "failed", "attempts": 2, "start": "2026-09-28T12:42:57.061887+00:00", "end": "2026-09-28T12:42:57.672300+00:00"}, "success_handler": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:42:57.864139+00:00", "end": "2026-09-28T12:42:57.864139+00:00"}}}
|
||||
{"label": "main_7_2_fail_second", "dag": "error_handling_dag", "run_id": "main_7_2_fail_second__1790599383044317774", "state": "failed", "expected_state": "failed", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:43:03.978190+00:00", "end": "2026-09-28T12:43:03.978208+00:00"}, "retry_task": {"state": "failed", "attempts": 2, "start": "2026-09-28T12:43:17.475284+00:00", "end": "2026-09-28T12:43:17.984522+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:43:05.869070+00:00", "end": "2026-09-28T12:43:06.372928+00:00"}, "success_handler": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:43:18.102304+00:00", "end": "2026-09-28T12:43:18.102304+00:00"}, "failure_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:43:19.615230+00:00", "end": "2026-09-28T12:43:20.101638+00:00"}}}
|
||||
{"label": "main_7_2_no_retry", "dag": "error_handling_dag", "run_id": "main_7_2_no_retry__1790599315199596055", "state": "failed", "expected_state": "failed", "tasks": {"failure_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:42:00.036017+00:00", "end": "2026-09-28T12:42:00.705269+00:00"}, "start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:41:56.214072+00:00", "end": "2026-09-28T12:41:56.214088+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:41:57.872256+00:00", "end": "2026-09-28T12:41:58.819669+00:00"}, "retry_task": {"state": "failed", "attempts": 1, "start": "2026-09-28T12:41:57.911390+00:00", "end": "2026-09-28T12:41:58.936653+00:00"}, "success_handler": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:41:59.040015+00:00", "end": "2026-09-28T12:41:59.040015+00:00"}}}
|
||||
{"label": "main_7_2_success", "dag": "error_handling_dag", "run_id": "main_7_2_success__1790599420340411753", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:43:40.702654+00:00", "end": "2026-09-28T12:43:40.702666+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:43:42.414466+00:00", "end": "2026-09-28T12:43:43.206132+00:00"}, "retry_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:43:42.347891+00:00", "end": "2026-09-28T12:43:43.281951+00:00"}, "failure_handler": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:43:43.383261+00:00", "end": "2026-09-28T12:43:43.383261+00:00"}, "success_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:43:44.696936+00:00", "end": "2026-09-28T12:43:45.165836+00:00"}}}
|
||||
{"label": "main_7_3", "dag": "error_handling_dag", "run_id": "main_7_3__1790599466582227262", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:44:27.093897+00:00", "end": "2026-09-28T12:44:27.093911+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:44:28.737493+00:00", "end": "2026-09-28T12:44:29.422467+00:00"}, "retry_task": {"state": "success", "attempts": 2, "start": "2026-09-28T12:44:40.106129+00:00", "end": "2026-09-28T12:44:40.590496+00:00"}, "failure_handler": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:44:40.639777+00:00", "end": "2026-09-28T12:44:40.639777+00:00"}, "success_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:44:41.109197+00:00", "end": "2026-09-28T12:44:41.576977+00:00"}}}
|
||||
{"label": "main_8_1_parallel", "dag": "resource_management_dag", "run_id": "main_8_1_parallel__1790599574558882106", "state": "success", "expected_state": "success", "tasks": {"read_orders": {"state": "success", "attempts": 1, "start": "2026-09-28T12:46:16.538315+00:00", "end": "2026-09-28T12:46:37.107764+00:00"}, "read_customers": {"state": "success", "attempts": 1, "start": "2026-09-28T12:46:16.596349+00:00", "end": "2026-09-28T12:46:37.192829+00:00"}, "calculate_metrics": {"state": "success", "attempts": 1, "start": "2026-09-28T12:46:37.808171+00:00", "end": "2026-09-28T12:46:38.458356+00:00"}, "log_metrics": {"state": "success", "attempts": 1, "start": "2026-09-28T12:46:39.331072+00:00", "end": "2026-09-28T12:46:39.899368+00:00"}}}
|
||||
{"label": "main_8_1_serial", "dag": "resource_management_dag", "run_id": "main_8_1_serial__1790599482620382943", "state": "success", "expected_state": "success", "tasks": {"log_metrics": {"state": "success", "attempts": 1, "start": "2026-09-28T12:45:29.173691+00:00", "end": "2026-09-28T12:45:30.128755+00:00"}, "read_customers": {"state": "success", "attempts": 1, "start": "2026-09-28T12:44:43.856681+00:00", "end": "2026-09-28T12:45:04.694013+00:00"}, "read_orders": {"state": "success", "attempts": 1, "start": "2026-09-28T12:45:05.830821+00:00", "end": "2026-09-28T12:45:26.382507+00:00"}, "calculate_metrics": {"state": "success", "attempts": 1, "start": "2026-09-28T12:45:27.408530+00:00", "end": "2026-09-28T12:45:28.420441+00:00"}}}
|
||||
{"label": "main_8_2", "dag": "resource_management_dag", "run_id": "main_8_2__1790599640804934985", "state": "success", "expected_state": "success", "tasks": {"read_customers": {"state": "success", "attempts": 1, "start": "2026-09-28T12:47:21.889563+00:00", "end": "2026-09-28T12:47:42.647916+00:00"}, "read_orders": {"state": "success", "attempts": 1, "start": "2026-09-28T12:47:43.632588+00:00", "end": "2026-09-28T12:48:04.391177+00:00"}, "calculate_metrics": {"state": "success", "attempts": 1, "start": "2026-09-28T12:48:05.904759+00:00", "end": "2026-09-28T12:48:06.622084+00:00"}, "log_metrics": {"state": "success", "attempts": 1, "start": "2026-09-28T12:48:08.049355+00:00", "end": "2026-09-28T12:48:08.564654+00:00"}}}
|
||||
{"label": "main_8_3", "dag": "resource_management_dag", "run_id": "main_8_3__1790599731782559761", "state": "success", "expected_state": "success", "tasks": {"extract.read_customers": {"state": "success", "attempts": 1, "start": "2026-09-28T12:48:53.427367+00:00", "end": "2026-09-28T12:49:13.903909+00:00"}, "extract.read_orders": {"state": "success", "attempts": 1, "start": "2026-09-28T12:49:15.274901+00:00", "end": "2026-09-28T12:49:35.809526+00:00"}, "calculate_metrics": {"state": "success", "attempts": 1, "start": "2026-09-28T12:49:36.965983+00:00", "end": "2026-09-28T12:49:37.918550+00:00"}, "log_metrics": {"state": "success", "attempts": 1, "start": "2026-09-28T12:49:39.405235+00:00", "end": "2026-09-28T12:49:39.974974+00:00"}}}
|
||||
{"label": "optional_1_A_first", "dag": "hello_world_dag", "run_id": "optional_1_A_first__1790599828501658295", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:50:29.705066+00:00", "end": "2026-09-28T12:51:00.256126+00:00"}, "date_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:02.651448+00:00", "end": "2026-09-28T12:51:03.175435+00:00"}, "weather_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:01.427549+00:00", "end": "2026-09-28T12:51:02.022120+00:00"}, "write_note": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:03.805590+00:00", "end": "2026-09-28T12:51:04.377917+00:00"}, "end_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:04.973858+00:00", "end": "2026-09-28T12:51:05.452179+00:00"}}}
|
||||
{"label": "optional_1_A_second", "dag": "hello_world_dag", "run_id": "optional_1_A_second__1790599831490826780", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:07.523497+00:00", "end": "2026-09-28T12:51:38.200910+00:00"}, "weather_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:40.092576+00:00", "end": "2026-09-28T12:51:40.720014+00:00"}, "date_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:42.001534+00:00", "end": "2026-09-28T12:51:42.531175+00:00"}, "end_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:45.023411+00:00", "end": "2026-09-28T12:51:45.554981+00:00"}, "write_note": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:43.392660+00:00", "end": "2026-09-28T12:51:44.179028+00:00"}}}
|
||||
{"label": "optional_2_A_B", "dag": "sql_basic_dag", "run_id": "optional_2_A_B__1790599987276042498", "state": "success", "expected_state": "success", "tasks": {"create_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:09.211357+00:00", "end": "2026-09-28T12:53:09.951512+00:00"}, "truncate_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:11.047425+00:00", "end": "2026-09-28T12:53:11.740876+00:00"}, "insert_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:12.934788+00:00", "end": "2026-09-28T12:53:13.607534+00:00"}, "update_student": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:15.944973+00:00", "end": "2026-09-28T12:53:16.638095+00:00"}, "query_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:17.435005+00:00", "end": "2026-09-28T12:53:18.141358+00:00"}, "report": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:19.546507+00:00", "end": "2026-09-28T12:53:20.286011+00:00"}}}
|
||||
{"label": "optional_3_A_B_C", "dag": "file_operations_dag", "run_id": "optional_3_A_B_C__1790600042332111688", "state": "success", "expected_state": "success", "tasks": {"transform_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:09.980335+00:00", "end": "2026-09-28T12:54:12.248114+00:00"}, "write_summary": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:13.879091+00:00", "end": "2026-09-28T12:54:17.284622+00:00"}, "read_and_validate_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:07.264422+00:00", "end": "2026-09-28T12:54:08.788050+00:00"}, "generate_sample_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:03.667581+00:00", "end": "2026-09-28T12:54:06.067366+00:00"}, "department_engineering": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:14.349510+00:00", "end": "2026-09-28T12:54:17.713422+00:00"}, "department_support": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:14.885206+00:00", "end": "2026-09-28T12:54:17.977159+00:00"}, "department_report": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:20.187672+00:00", "end": "2026-09-28T12:54:21.852688+00:00"}, "department_sales": {"state": "success", "attempts": 2, "start": "2026-09-28T12:54:20.381886+00:00", "end": "2026-09-28T12:54:22.016177+00:00"}, "gather": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:22.800577+00:00", "end": "2026-09-28T12:54:24.457104+00:00"}}}
|
||||
{"label": "optional_4_A_dq", "dag": "csv_to_postgres_dq", "run_id": "optional_4_A_dq__1790600084423979625", "state": "success", "expected_state": "success", "tasks": {"check_orders_table_exists": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:45.792417+00:00", "end": "2026-09-28T12:54:46.301310+00:00"}, "check_orders_schema": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:47.351405+00:00", "end": "2026-09-28T12:54:47.874188+00:00"}, "check_orders_has_rows": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:48.529849+00:00", "end": "2026-09-28T12:54:48.966554+00:00"}, "check_order_duplicates": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:49.787486+00:00", "end": "2026-09-28T12:54:50.271862+00:00"}, "check_positive_amount": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:51.064045+00:00", "end": "2026-09-28T12:54:51.705791+00:00"}, "data_quality_summary": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:52.304665+00:00", "end": "2026-09-28T12:54:52.846778+00:00"}}}
|
||||
{"label": "optional_4_A_restore", "dag": "csv_to_postgres_dq", "run_id": "optional_4_A_restore__1790600176128130911", "state": "success", "expected_state": "success", "tasks": {"check_orders_schema": {"state": "success", "attempts": 1, "start": "2026-09-28T12:56:18.907596+00:00", "end": "2026-09-28T12:56:19.356101+00:00"}, "check_orders_table_exists": {"state": "success", "attempts": 1, "start": "2026-09-28T12:56:17.659452+00:00", "end": "2026-09-28T12:56:18.088407+00:00"}, "check_positive_amount": {"state": "success", "attempts": 1, "start": "2026-09-28T12:56:22.339147+00:00", "end": "2026-09-28T12:56:22.756030+00:00"}, "check_orders_has_rows": {"state": "success", "attempts": 1, "start": "2026-09-28T12:56:19.993271+00:00", "end": "2026-09-28T12:56:20.456629+00:00"}, "check_order_duplicates": {"state": "success", "attempts": 1, "start": "2026-09-28T12:56:21.187257+00:00", "end": "2026-09-28T12:56:21.591291+00:00"}, "data_quality_summary": {"state": "success", "attempts": 1, "start": "2026-09-28T12:56:23.525671+00:00", "end": "2026-09-28T12:56:23.996173+00:00"}}}
|
||||
{"label": "optional_4_A_status", "dag": "csv_to_postgres", "run_id": "optional_4_A_status__1790600072998689452", "state": "success", "expected_state": "success", "tasks": {"create_orders_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:34.420941+00:00", "end": "2026-09-28T12:54:34.965465+00:00"}, "generate_csv": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:35.846349+00:00", "end": "2026-09-28T12:54:37.632934+00:00"}, "load_csv_to_postgres": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:40.758903+00:00", "end": "2026-09-28T12:54:41.313249+00:00"}, "preview_csv": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:38.368799+00:00", "end": "2026-09-28T12:54:39.934048+00:00"}}}
|
||||
{"label": "optional_4_A_status_bad_order", "dag": "csv_to_postgres", "run_id": "optional_4_A_status__1790599866797382800", "state": "failed", "expected_state": "failed", "tasks": {"create_orders_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:07.993200+00:00", "end": "2026-09-28T12:51:08.577937+00:00"}, "load_csv_to_postgres": {"state": "failed", "attempts": 2, "start": "2026-09-28T12:51:47.478501+00:00", "end": "2026-09-28T12:51:48.034511+00:00"}, "generate_csv": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:09.311281+00:00", "end": "2026-09-28T12:51:10.967148+00:00"}, "preview_csv": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:12.691761+00:00", "end": "2026-09-28T12:51:14.377415+00:00"}}}
|
||||
{"label": "optional_4_B_dq", "dag": "csv_to_postgres_dq", "run_id": "optional_4_B_dq__1790600237996456220", "state": "success", "expected_state": "success", "tasks": {"check_orders_table_exists": {"state": "success", "attempts": 1, "start": "2026-09-28T12:57:19.103267+00:00", "end": "2026-09-28T12:57:19.560193+00:00"}, "check_orders_schema": {"state": "success", "attempts": 1, "start": "2026-09-28T12:57:20.324388+00:00", "end": "2026-09-28T12:57:20.846060+00:00"}, "check_orders_has_rows": {"state": "success", "attempts": 1, "start": "2026-09-28T12:57:22.250197+00:00", "end": "2026-09-28T12:57:22.727576+00:00"}, "check_order_duplicates": {"state": "success", "attempts": 1, "start": "2026-09-28T12:57:23.415948+00:00", "end": "2026-09-28T12:57:23.951371+00:00"}, "check_positive_amount": {"state": "success", "attempts": 1, "start": "2026-09-28T12:57:25.207786+00:00", "end": "2026-09-28T12:57:25.689645+00:00"}, "data_quality_summary": {"state": "success", "attempts": 1, "start": "2026-09-28T12:57:27.492209+00:00", "end": "2026-09-28T12:57:28.083003+00:00"}}}
|
||||
{"label": "optional_4_B_operator", "dag": "csv_to_postgres", "run_id": "optional_4_B_operator__1790600227029157362", "state": "success", "expected_state": "success", "tasks": {"generate_csv": {"state": "success", "attempts": 1, "start": "2026-09-28T12:57:10.252580+00:00", "end": "2026-09-28T12:57:11.814673+00:00"}, "create_orders_table": {"state": "success", "attempts": 1, "start": "2026-09-28T12:57:08.082255+00:00", "end": "2026-09-28T12:57:08.667507+00:00"}, "preview_csv": {"state": "success", "attempts": 1, "start": "2026-09-28T12:57:13.073106+00:00", "end": "2026-09-28T12:57:14.567664+00:00"}, "load_csv_to_postgres": {"state": "success", "attempts": 1, "start": "2026-09-28T12:57:15.203959+00:00", "end": "2026-09-28T12:57:15.691633+00:00"}}}
|
||||
{"label": "optional_4_C_all_done", "dag": "csv_to_postgres_dq", "run_id": "optional_4_C_all_done__1790600337228090134", "state": "failed", "expected_state": "failed", "tasks": {"check_orders_table_exists": {"state": "success", "attempts": 1, "start": "2026-09-28T12:58:58.725586+00:00", "end": "2026-09-28T12:58:59.201374+00:00"}, "check_orders_schema": {"state": "failed", "attempts": 1, "start": "2026-09-28T12:59:01.015357+00:00", "end": "2026-09-28T12:59:01.541150+00:00"}, "check_orders_has_rows": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:02.461634+00:00", "end": "2026-09-28T12:59:02.991782+00:00"}, "check_order_duplicates": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:03.664066+00:00", "end": "2026-09-28T12:59:04.142534+00:00"}, "check_positive_amount": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:04.892560+00:00", "end": "2026-09-28T12:59:05.430995+00:00"}, "data_quality_summary": {"state": "failed", "attempts": 1, "start": "2026-09-28T12:59:07.120230+00:00", "end": "2026-09-28T12:59:07.593930+00:00"}}}
|
||||
{"label": "optional_4_C_default", "dag": "csv_to_postgres_dq", "run_id": "optional_4_C_default__1790600291366135652", "state": "failed", "expected_state": "failed", "tasks": {"check_order_duplicates": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:58:15.164590+00:00", "end": "2026-09-28T12:58:15.164590+00:00"}, "check_orders_table_exists": {"state": "success", "attempts": 1, "start": "2026-09-28T12:58:13.042381+00:00", "end": "2026-09-28T12:58:13.526508+00:00"}, "check_positive_amount": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:58:16.262284+00:00", "end": "2026-09-28T12:58:16.262284+00:00"}, "data_quality_summary": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:58:16.585583+00:00", "end": "2026-09-28T12:58:16.585583+00:00"}, "check_orders_schema": {"state": "failed", "attempts": 1, "start": "2026-09-28T12:58:14.330420+00:00", "end": "2026-09-28T12:58:14.851567+00:00"}, "check_orders_has_rows": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:58:14.960806+00:00", "end": "2026-09-28T12:58:14.960806+00:00"}}}
|
||||
{"label": "optional_4_C_restore", "dag": "csv_to_postgres_dq", "run_id": "optional_4_C_restore__1790600390711838382", "state": "success", "expected_state": "success", "tasks": {"check_orders_table_exists": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:51.882799+00:00", "end": "2026-09-28T12:59:52.351329+00:00"}, "check_orders_schema": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:53.403783+00:00", "end": "2026-09-28T12:59:53.992901+00:00"}, "check_orders_has_rows": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:54.968165+00:00", "end": "2026-09-28T12:59:55.731129+00:00"}, "check_order_duplicates": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:58.083402+00:00", "end": "2026-09-28T12:59:59.365498+00:00"}, "check_positive_amount": {"state": "success", "attempts": 1, "start": "2026-09-28T13:00:01.904918+00:00", "end": "2026-09-28T13:00:03.145987+00:00"}, "data_quality_summary": {"state": "success", "attempts": 1, "start": "2026-09-28T13:00:03.953750+00:00", "end": "2026-09-28T13:00:04.515677+00:00"}}}
|
||||
{"label": "optional_5_A_B_C_success", "dag": "data_processing_dag", "run_id": "optional_5_A_B_C_success__1790599826280464970", "state": "success", "expected_state": "success", "tasks": {"create_sample_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:50:28.065679+00:00", "end": "2026-09-28T12:50:29.967444+00:00"}, "validate": {"state": "success", "attempts": 1, "start": "2026-09-28T12:50:38.601847+00:00", "end": "2026-09-28T12:50:40.510529+00:00"}, "transform_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:50:35.381953+00:00", "end": "2026-09-28T12:50:37.142909+00:00"}, "extract_customers": {"state": "success", "attempts": 1, "start": "2026-09-28T12:50:30.926518+00:00", "end": "2026-09-28T12:50:32.669276+00:00"}, "extract_products": {"state": "success", "attempts": 1, "start": "2026-09-28T12:50:31.062084+00:00", "end": "2026-09-28T12:50:32.916827+00:00"}, "extract_orders": {"state": "success", "attempts": 1, "start": "2026-09-28T12:50:30.983002+00:00", "end": "2026-09-28T12:50:33.137312+00:00"}, "simulate_load": {"state": "success", "attempts": 1, "start": "2026-09-28T12:50:41.199488+00:00", "end": "2026-09-28T12:50:42.824662+00:00"}, "generate_report": {"state": "success", "attempts": 1, "start": "2026-09-28T12:50:43.422545+00:00", "end": "2026-09-28T12:50:45.474906+00:00"}, "archive": {"state": "success", "attempts": 1, "start": "2026-09-28T12:50:46.874833+00:00", "end": "2026-09-28T12:50:47.500464+00:00"}}}
|
||||
{"label": "optional_5_C_failure", "dag": "data_processing_dag", "run_id": "optional_5_C_failure__1790599890880680316", "state": "failed", "expected_state": "failed", "tasks": {"create_sample_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:31.769851+00:00", "end": "2026-09-28T12:51:33.331037+00:00"}, "extract_orders": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:34.907501+00:00", "end": "2026-09-28T12:51:36.969211+00:00"}, "extract_customers": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:34.860135+00:00", "end": "2026-09-28T12:51:37.189529+00:00"}, "transform_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:38.760107+00:00", "end": "2026-09-28T12:51:40.621671+00:00"}, "simulate_load": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:51:49.189667+00:00", "end": "2026-09-28T12:51:49.189667+00:00"}, "generate_report": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:51:49.662176+00:00", "end": "2026-09-28T12:51:49.662176+00:00"}, "archive": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:51:50.496425+00:00", "end": "2026-09-28T12:51:50.496425+00:00"}, "extract_products": {"state": "success", "attempts": 1, "start": "2026-09-28T12:51:35.050525+00:00", "end": "2026-09-28T12:51:37.258136+00:00"}, "validate": {"state": "failed", "attempts": 2, "start": "2026-09-28T12:51:47.454480+00:00", "end": "2026-09-28T12:51:49.033534+00:00"}}}
|
||||
{"label": "optional_5_C_restore", "dag": "data_processing_dag", "run_id": "optional_5_C_restore__1790599954560978695", "state": "success", "expected_state": "success", "tasks": {"extract_orders": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:38.792872+00:00", "end": "2026-09-28T12:52:41.056815+00:00"}, "extract_customers": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:38.712862+00:00", "end": "2026-09-28T12:52:40.983042+00:00"}, "transform_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:44.098377+00:00", "end": "2026-09-28T12:52:46.183169+00:00"}, "generate_report": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:54.485567+00:00", "end": "2026-09-28T12:52:56.742958+00:00"}, "archive": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:57.823277+00:00", "end": "2026-09-28T12:52:58.453906+00:00"}, "create_sample_data": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:35.864954+00:00", "end": "2026-09-28T12:52:37.451156+00:00"}, "extract_products": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:38.920025+00:00", "end": "2026-09-28T12:52:41.149145+00:00"}, "validate": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:47.821696+00:00", "end": "2026-09-28T12:52:49.319829+00:00"}, "simulate_load": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:50.127366+00:00", "end": "2026-09-28T12:52:52.548589+00:00"}}}
|
||||
{"label": "optional_6_A_csv", "dag": "branching_dag", "run_id": "optional_6_A_csv__1790599969413452767", "state": "success", "expected_state": "success", "tasks": {"process_csv_branch": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:54.817429+00:00", "end": "2026-09-28T12:52:55.475004+00:00"}, "start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:52:49.756524+00:00", "end": "2026-09-28T12:52:49.756537+00:00"}, "process_json_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:52:53.426676+00:00", "end": "2026-09-28T12:52:53.426676+00:00"}, "choose_format": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:51.852609+00:00", "end": "2026-09-28T12:52:53.700004+00:00"}, "review_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:52:53.426676+00:00", "end": "2026-09-28T12:52:53.426676+00:00"}, "merge_results": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:56.749010+00:00", "end": "2026-09-28T12:52:57.301418+00:00"}, "end_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:52:57.406094+00:00", "end": "2026-09-28T12:52:57.406105+00:00"}}}
|
||||
{"label": "optional_6_A_json", "dag": "branching_dag", "run_id": "optional_6_A_json__1790599981318891965", "state": "success", "expected_state": "success", "tasks": {"process_csv_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:53:04.678219+00:00", "end": "2026-09-28T12:53:04.678219+00:00"}, "review_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:53:04.678219+00:00", "end": "2026-09-28T12:53:04.678219+00:00"}, "start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:53:02.190235+00:00", "end": "2026-09-28T12:53:02.190247+00:00"}, "choose_format": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:03.854653+00:00", "end": "2026-09-28T12:53:04.883167+00:00"}, "process_json_branch": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:05.617301+00:00", "end": "2026-09-28T12:53:06.163931+00:00"}, "merge_results": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:06.846567+00:00", "end": "2026-09-28T12:53:07.431430+00:00"}, "end_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:53:07.527378+00:00", "end": "2026-09-28T12:53:07.527385+00:00"}}}
|
||||
{"label": "optional_6_A_review", "dag": "branching_dag", "run_id": "optional_6_A_review__1790599950878181492", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:52:31.880500+00:00", "end": "2026-09-28T12:52:31.880513+00:00"}, "process_csv_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:52:34.520952+00:00", "end": "2026-09-28T12:52:34.520952+00:00"}, "process_json_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:52:34.520952+00:00", "end": "2026-09-28T12:52:34.520952+00:00"}, "choose_format": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:33.686169+00:00", "end": "2026-09-28T12:52:34.634325+00:00"}, "review_branch": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:35.883342+00:00", "end": "2026-09-28T12:52:36.335243+00:00"}, "merge_results": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:37.145269+00:00", "end": "2026-09-28T12:52:37.638569+00:00"}, "end_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:52:38.080346+00:00", "end": "2026-09-28T12:52:38.080354+00:00"}}}
|
||||
{"label": "optional_6_A_xml", "dag": "branching_dag", "run_id": "optional_6_A_xml__1790599960064387143", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:52:40.576461+00:00", "end": "2026-09-28T12:52:40.576475+00:00"}, "process_csv_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:52:42.254102+00:00", "end": "2026-09-28T12:52:42.254102+00:00"}, "process_json_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:52:42.254102+00:00", "end": "2026-09-28T12:52:42.254102+00:00"}, "choose_format": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:41.714877+00:00", "end": "2026-09-28T12:52:42.372355+00:00"}, "review_branch": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:44.072978+00:00", "end": "2026-09-28T12:52:44.611876+00:00"}, "end_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:52:47.349562+00:00", "end": "2026-09-28T12:52:47.349570+00:00"}, "merge_results": {"state": "success", "attempts": 1, "start": "2026-09-28T12:52:45.532475+00:00", "end": "2026-09-28T12:52:46.387047+00:00"}}}
|
||||
{"label": "optional_6_B_csv", "dag": "branching_dag", "run_id": "optional_6_B_csv__1790600030982481228", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:53:51.382625+00:00", "end": "2026-09-28T12:53:51.382631+00:00"}, "process_json_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:53:53.983930+00:00", "end": "2026-09-28T12:53:53.983930+00:00"}, "review_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:53:53.983930+00:00", "end": "2026-09-28T12:53:53.983930+00:00"}, "choose_format": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:52.593521+00:00", "end": "2026-09-28T12:53:54.169523+00:00"}, "process_csv_branch": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:55.338055+00:00", "end": "2026-09-28T12:53:57.034412+00:00"}, "end_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:53:58.733441+00:00", "end": "2026-09-28T12:53:58.733456+00:00"}, "merge_results": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:58.016770+00:00", "end": "2026-09-28T12:53:58.536435+00:00"}}}
|
||||
{"label": "optional_6_B_json", "dag": "branching_dag", "run_id": "optional_6_B_json__1790600043123594630", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:54:04.312564+00:00", "end": "2026-09-28T12:54:04.312573+00:00"}, "process_csv_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:54:07.267935+00:00", "end": "2026-09-28T12:54:07.267935+00:00"}, "review_branch": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:54:07.267935+00:00", "end": "2026-09-28T12:54:07.267935+00:00"}, "choose_format": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:06.715738+00:00", "end": "2026-09-28T12:54:07.404010+00:00"}, "process_json_branch": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:08.623885+00:00", "end": "2026-09-28T12:54:10.327426+00:00"}, "merge_results": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:11.590619+00:00", "end": "2026-09-28T12:54:12.518040+00:00"}, "end_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:54:13.240380+00:00", "end": "2026-09-28T12:54:13.240393+00:00"}}}
|
||||
{"label": "optional_7_A", "dag": "error_handling_dag", "run_id": "optional_7_A__1790600019449470468", "state": "failed", "expected_state": "failed", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:53:40.237917+00:00", "end": "2026-09-28T12:53:40.237924+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:53:41.595590+00:00", "end": "2026-09-28T12:53:42.183338+00:00"}, "success_handler": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:54:50.391456+00:00", "end": "2026-09-28T12:54:50.391456+00:00"}, "failure_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:54:51.055581+00:00", "end": "2026-09-28T12:54:51.678943+00:00"}, "retry_task": {"state": "failed", "attempts": 4, "start": "2026-09-28T12:54:49.753125+00:00", "end": "2026-09-28T12:54:50.277862+00:00"}}}
|
||||
{"label": "optional_7_B_ConnectionError", "dag": "error_handling_dag", "run_id": "optional_7_B_ConnectionError__1790600133593914390", "state": "failed", "expected_state": "failed", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:55:34.970055+00:00", "end": "2026-09-28T12:55:34.970064+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:55:36.562237+00:00", "end": "2026-09-28T12:55:37.011257+00:00"}, "retry_task": {"state": "failed", "attempts": 2, "start": "2026-09-28T12:55:47.492118+00:00", "end": "2026-09-28T12:55:47.940804+00:00"}, "success_handler": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:55:48.064846+00:00", "end": "2026-09-28T12:55:48.064846+00:00"}, "failure_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:55:48.712490+00:00", "end": "2026-09-28T12:55:49.197555+00:00"}}}
|
||||
{"label": "optional_7_B_OSError", "dag": "error_handling_dag", "run_id": "optional_7_B_OSError__1790600252561937372", "state": "failed", "expected_state": "failed", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:57:33.632472+00:00", "end": "2026-09-28T12:57:33.632487+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:57:34.290109+00:00", "end": "2026-09-28T12:57:34.768072+00:00"}, "retry_task": {"state": "failed", "attempts": 2, "start": "2026-09-28T12:57:45.543523+00:00", "end": "2026-09-28T12:57:46.128267+00:00"}, "success_handler": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:57:46.246909+00:00", "end": "2026-09-28T12:57:46.246909+00:00"}, "failure_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:57:47.071181+00:00", "end": "2026-09-28T12:57:47.715989+00:00"}}}
|
||||
{"label": "optional_7_B_ValueError", "dag": "error_handling_dag", "run_id": "optional_7_B_ValueError__1790600191959984913", "state": "failed", "expected_state": "failed", "tasks": {"success_handler": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:56:45.784424+00:00", "end": "2026-09-28T12:56:45.784424+00:00"}, "failure_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:56:46.689244+00:00", "end": "2026-09-28T12:56:47.474193+00:00"}, "start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:56:32.295591+00:00", "end": "2026-09-28T12:56:32.295598+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:56:33.808507+00:00", "end": "2026-09-28T12:56:34.327861+00:00"}, "retry_task": {"state": "failed", "attempts": 2, "start": "2026-09-28T12:56:44.934551+00:00", "end": "2026-09-28T12:56:45.531229+00:00"}}}
|
||||
{"label": "optional_7_B_restore", "dag": "error_handling_dag", "run_id": "optional_7_B_restore__1790600308604083181", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:58:28.905883+00:00", "end": "2026-09-28T12:58:28.905893+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:58:30.479933+00:00", "end": "2026-09-28T12:58:30.955552+00:00"}, "success_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:58:44.841191+00:00", "end": "2026-09-28T12:58:45.414819+00:00"}, "retry_task": {"state": "success", "attempts": 2, "start": "2026-09-28T12:58:42.378837+00:00", "end": "2026-09-28T12:58:43.211009+00:00"}, "failure_handler": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:58:43.325326+00:00", "end": "2026-09-28T12:58:43.325326+00:00"}}}
|
||||
{"label": "optional_8_A", "dag": "resource_management_dag", "run_id": "optional_8_A__1790600366934777325", "state": "success", "expected_state": "success", "tasks": {"extract.read_customers": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:28.045365+00:00", "end": "2026-09-28T12:59:48.565983+00:00"}, "extract.read_orders": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:49.448034+00:00", "end": "2026-09-28T13:00:10.063529+00:00"}, "calculate_metrics": {"state": "success", "attempts": 1, "start": "2026-09-28T13:00:11.157140+00:00", "end": "2026-09-28T13:00:11.708381+00:00"}, "print_total": {"state": "success", "attempts": 1, "start": "2026-09-28T13:00:12.427443+00:00", "end": "2026-09-28T13:00:13.032322+00:00"}, "log_metrics": {"state": "success", "attempts": 1, "start": "2026-09-28T13:00:12.452891+00:00", "end": "2026-09-28T13:00:12.951344+00:00"}}}
|
||||
{"label": "optional_8_B_delivery_error_failed", "dag": "error_handling_dag", "run_id": "optional_8_B_delivery_error_failed__1790600442808275037", "state": "failed", "expected_state": "failed", "tasks": {"retry_task": {"state": "failed", "attempts": 2, "start": "2026-09-28T13:00:55.863913+00:00", "end": "2026-09-28T13:00:56.344315+00:00"}, "success_handler": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T13:00:56.517274+00:00", "end": "2026-09-28T13:00:56.517274+00:00"}, "start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T13:00:43.241323+00:00", "end": "2026-09-28T13:00:43.241338+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T13:00:44.542395+00:00", "end": "2026-09-28T13:00:45.057656+00:00"}, "failure_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T13:00:58.115826+00:00", "end": "2026-09-28T13:00:58.719339+00:00"}}}
|
||||
{"label": "optional_8_B_delivery_error_success", "dag": "error_handling_dag", "run_id": "optional_8_B_delivery_error_success__1790600461566675460", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T13:01:02.553513+00:00", "end": "2026-09-28T13:01:02.553524+00:00"}, "success_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T13:01:04.593955+00:00", "end": "2026-09-28T13:01:05.213490+00:00"}, "retry_task": {"state": "success", "attempts": 1, "start": "2026-09-28T13:01:03.364187+00:00", "end": "2026-09-28T13:01:03.838482+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T13:01:03.355462+00:00", "end": "2026-09-28T13:01:03.840328+00:00"}, "failure_handler": {"state": "skipped", "attempts": 0, "start": "2026-09-28T13:01:03.939908+00:00", "end": "2026-09-28T13:01:03.939908+00:00"}}}
|
||||
{"label": "optional_8_B_failure", "dag": "error_handling_dag", "run_id": "optional_8_B_failure__1790600374167341045", "state": "failed", "expected_state": "failed", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:59:34.633405+00:00", "end": "2026-09-28T12:59:34.633414+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:36.262871+00:00", "end": "2026-09-28T12:59:36.837096+00:00"}, "retry_task": {"state": "failed", "attempts": 2, "start": "2026-09-28T12:59:47.584630+00:00", "end": "2026-09-28T12:59:48.117663+00:00"}, "success_handler": {"state": "upstream_failed", "attempts": 0, "start": "2026-09-28T12:59:48.256654+00:00", "end": "2026-09-28T12:59:48.256654+00:00"}, "failure_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:49.504044+00:00", "end": "2026-09-28T12:59:50.105201+00:00"}}}
|
||||
{"label": "optional_8_B_restore", "dag": "error_handling_dag", "run_id": "optional_8_B_restore__1790600507926543810", "state": "success", "expected_state": "success", "tasks": {"start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T13:01:49.154391+00:00", "end": "2026-09-28T13:01:49.154407+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T13:01:50.686480+00:00", "end": "2026-09-28T13:01:51.192154+00:00"}, "retry_task": {"state": "success", "attempts": 2, "start": "2026-09-28T13:02:01.992429+00:00", "end": "2026-09-28T13:02:02.590182+00:00"}, "success_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T13:02:03.292271+00:00", "end": "2026-09-28T13:02:03.992153+00:00"}, "failure_handler": {"state": "skipped", "attempts": 0, "start": "2026-09-28T13:02:02.684938+00:00", "end": "2026-09-28T13:02:02.684938+00:00"}}}
|
||||
{"label": "optional_8_B_success", "dag": "error_handling_dag", "run_id": "optional_8_B_success__1790600393174553780", "state": "success", "expected_state": "success", "tasks": {"success_handler": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:58.041525+00:00", "end": "2026-09-28T12:59:59.107268+00:00"}, "start_task": {"state": "success", "attempts": 0, "start": "2026-09-28T12:59:54.153604+00:00", "end": "2026-09-28T12:59:54.153613+00:00"}, "unreliable_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:54.985172+00:00", "end": "2026-09-28T12:59:55.734497+00:00"}, "retry_task": {"state": "success", "attempts": 1, "start": "2026-09-28T12:59:55.307245+00:00", "end": "2026-09-28T12:59:55.926514+00:00"}, "failure_handler": {"state": "skipped", "attempts": 0, "start": "2026-09-28T12:59:56.121310+00:00", "end": "2026-09-28T12:59:56.121310+00:00"}}}
|
||||
@@ -0,0 +1,69 @@
|
||||
# Проверка обновленной практики
|
||||
|
||||
Дата: 2026-09-28. Код примеров: коммит `cf1b770`; задания и навигация: `d8dfe83`. Проверка выполнена авторским прогоном по [заданиям](../../airflow-docker/educational-tasks.md). Это проверка воспроизводимости инструкций; время и трудность прохождения на студентах не измерялись.
|
||||
|
||||
## Окружение
|
||||
|
||||
Образ собран из Dockerfile стенда. Фактические версии внутри контейнера: Apache Airflow 2.9.2, Python 3.12.4, pandas 2.1.4, mimesis 15.1.0, PostgreSQL provider 5.11.1. Executor: LocalExecutor, PostgreSQL 16.
|
||||
|
||||
Использован отдельный Compose-проект `airflow-manual-practice-check`: собственные новые тома БД и копии `dags/`, `data/`. Порты перенесены на 18089, 15439, 15440. Для автоматизации включен Basic Auth API, `parallelism=4`, интервалы обнаружения файлов сокращены до 5 секунд. Перед запуском измененного DAG проверялся новый разбор файла и выдерживался интервал обновления сериализованного графа.
|
||||
|
||||
Изменения студента выполнялись только в копиях. Внутри блока они сохранялись. Вначале прошли исходные примеры, затем основной маршрут без дополнительных заданий. Дополнительные расширения проверены после него. Независимые блоки выполнялись одновременно; загрузчик и DQ шли последовательно.
|
||||
|
||||
Повторный `docker compose run --rm airflow-init` на существующих томах также завершился с кодом 0: учетная запись сохранена, подключение пересоздано.
|
||||
|
||||
## Исходные примеры
|
||||
|
||||
Все девять DAG загрузились без Import Errors и завершились успешно. Для `resource_management_dag` заранее создан пул из двух слотов. У `error_handling_dag` режим по умолчанию дал ошибку первой попытки и успех второй. У ветвления выбранная ветка успешна, другая пропущена.
|
||||
|
||||
SQL оставил трех студентов, загрузчик создал 1000 заказов, DQ прошел. Файловый пример записал 100 строк. ETL создал шесть заказов с общей суммой 2105, шаг `simulate_load` сообщил об имитации.
|
||||
|
||||
## Основной маршрут
|
||||
|
||||
| Упражнения | Действия и фактический результат |
|
||||
|---|---|
|
||||
| 1.1-1.3 | После добавления погоды четыре задачи, после BashOperator пять; `note.txt` содержит `Airflow works`. Расписание изменилось на `0 0 * * 1`, ручной запуск прошел. |
|
||||
| 2.1-2.2 | Создано отдельное подключение. Неверный Conn Id при нуле повторов: `create_table=failed`, следующие задачи `upstream_failed`. После восстановления и UPDATE: Alice 25, Bob 24, Charlie 21. Повторный полный запуск оставил три строки. |
|
||||
| 3.1 | Порог High изменен на 80000. Проверены 100 строк и соответствие категории зарплате в CSV того же запуска. |
|
||||
| 4.1-4.2 | Второй последовательный запуск загрузчика увеличил число заказов до 2000. Добавлена проверка суммы: корректные данные проходят; один отрицательный заказ дает `check_positive_amount=failed`, сводка `upstream_failed`, DAG Run `failed`. Повторный UPDATE восстановил успешный DQ. |
|
||||
| 5.1 | Фильтр в Extract дал 4 заказа и сумму 2000 в результате Transform. После удаления фильтра снова 6 и 2105. |
|
||||
| 6.1 | Режимы `csv`, `json`, `xml`, затем `csv`: выбор нужной ветки, отказ при неизвестном формате и успешное восстановление. XCom слияния относится к текущему запуску. |
|
||||
| 7.1-7.3 | `retry_once`: две попытки и успех; переопределение `retries=0`: окончательный отказ. `fail_first`, `fail_second`, `fail_both`: обработчик ошибки успешен, обработчик успеха `upstream_failed`, DAG Run `failed`. Режим `success` и возврат к `retry_once` успешны. Добавлена запись в лог обработчика успеха. |
|
||||
| 8.1 | Интервалы выполнения задач с весами 2 и 1 не пересекаются. После замены веса 2 на 1 интервалы пересекаются. |
|
||||
| 8.2-8.3 | Добавлена метрика `total_rows=50`. После TaskGroup обновлены оба полных task_id, все четыре задачи успешны; XCom сохранил 20, 30 и 50. |
|
||||
|
||||
Основной маршрут завершен без дополнительных упражнений.
|
||||
|
||||
## Дополнительные упражнения
|
||||
|
||||
| Упражнения | Действия и фактический результат |
|
||||
|---|---|
|
||||
| 1.A | Два ручных запуска с задержкой 30 секунд и `max_active_runs=1`: задачи второго начались после завершения первого. Затем задержка удалена, DAG выключен. |
|
||||
| 2.A-2.B | Колонка email добавлена через ALTER до запуска. Три адреса заполнены, отчет содержит 3 студента и средний возраст 23.33; UPDATE из основной практики сохранен. |
|
||||
| 3.A-3.C | Новые поля проверены, файл опытных сотрудников отделен от полного. Численность отделов в JSON дает 100; сборка трех файлов содержит те же 100 уникальных id. Отдел sales выполнился со второй попытки без дублей. |
|
||||
| 4.A | При неверном порядке CSV/COPY получено `invalid input syntax for type numeric: "NEW"`. После согласования порядка новые статусы попали в БД и прошел DQ. После удаления поля из кода и таблицы DQ снова успешен; положительная сумма продолжает проверяться. |
|
||||
| 4.B-4.C | Создание таблицы заменено PostgresOperator, загрузка и DQ успешны. Неверный ожидаемый тип amount при all_success дал upstream_failed у следующих задач. С all_done они выполнились, но сводка и DAG Run остались failed. Возврат numeric и исходных trigger rules восстановил успешный DQ. |
|
||||
| 5.A-5.C | Справочник продуктов не изменил 6 заказов и сумму 2105. Топ-3: 1200, 400, 300; средний заказ Alice 640. Неверный клиент вызвал отказ проверки и заблокировал архив. После восстановления появился один архив, побайтово равный результату. |
|
||||
| 6.A-6.B | Третья ветка выбрана для review и неизвестного формата; csv/json продолжают работать. Чтение двух фиксированных файлов дало по 2 записи с суммой 30 в отдельных запусках. |
|
||||
| 7.A | Три повтора дали четыре неуспешные попытки, после которых DAG Run остался failed. |
|
||||
| 7.B | ConnectionError, ValueError и OSError вызвали callback после второй попытки; проверены task_id и текст причины. В обеих попытках восстановленного retry_once callback отказа отсутствует. |
|
||||
| 8.A | После группировки добавлен BashOperator с чтением XCom; в Details видна команда echo 50, в Logs число 50 и успешный выход. |
|
||||
| 8.B | Локальный SMTP принял одно письмо об отказе retry_task и одно об успехе DAG. При недоступном SMTP неуспешный запуск остался failed, успешный остался success. После отключения почтовых callbacks и возврата retry_once запуск успешен. |
|
||||
|
||||
[Протокол запусков JSONL](2026-09-28-practice-runs.jsonl) содержит 69 контрольных запусков и один диагностический запуск с неверным порядком CSV/COPY. Для каждого сохранены ожидаемый и фактический итог, состояния и число попыток задач, время начала и окончания. В первой строке указаны версии и SHA-256 итоговых DAG. Все ожидаемые состояния сошлись.
|
||||
|
||||
При выполнении уточнены инструкции: одинаковый порядок CSV/COPY; короткая задержка повтора в 3.C и 5.C; расположение Rendered Templates внутри Details; раскрытие TaskGroup нажатием на название со стрелкой. Сообщение callback оператора подтверждено в логе последней попытки. Команда `docker compose logs airflow-scheduler` его не показала. Отправка из callback DAG и ошибка его доставки найдены в `/opt/airflow/logs/scheduler/latest/error_handling_dag.py.log`.
|
||||
|
||||
## Интерфейс и документы
|
||||
|
||||
Chromium: вход под учебной учетной записью, список девяти DAG, запуск кнопкой Trigger DAG, граф с четырьмя прямыми ребрами от рабочих задач к обработчикам. В Logs доступны две попытки; во второй видно `Starting attempt 2 of 2` и сообщение об успехе. В XCom загрузчика найден путь к CSV; тот же CSV виден в Details, раздел Rendered Templates, у задачи загрузки. Часть пути там маскируется как `***`; инструкция уточнена, имя файла сверено с XCom.
|
||||
|
||||
Снимки проверенных состояний: [окончательный отказ](../../_attachments/practice-failed-run.png), [раскрытая TaskGroup](../../_attachments/practice-task-group.png), [путь в Rendered Templates](../../_attachments/practice-rendered-template.png). Группа раскрыта нажатием на название со стрелкой, внутри видны обе задачи.
|
||||
|
||||
Markdown отрисован отдельно: восемь строк маршрута, 34 заголовка упражнений, рабочий переход к блоку ошибок. Проверены локальные ссылки и якоря в измененных документах, синтаксис девяти DAG и `git diff --check`.
|
||||
|
||||
После сохранения протокола изолированные контейнеры, браузер и два тестовых тома удалены. Остальные работающие сервисы не изменялись.
|
||||
|
||||
## Границы проверки
|
||||
|
||||
Почтовый пример проверен на локальном SMTP-приемнике без отправки сообщений третьим лицам. Внешний провайдер, авторизация и TLS не проверялись: их параметры зависят от выбранного студентом сервера. Поведение Windows не проверялось на отдельной Windows-машине; опубликованные команды не используют синтаксис, специфичный для Bash.
|
||||
Reference in new issue
Block a user