- Зачем: - необходимо визуализировать выбор типа хранения (Heap vs Append-Only) для учебных целей. - автоматизировать проверку всей цепочки DWH для исключения ручных ошибок. - сделать процесс отладки прозрачным и наглядным через стандартные инструменты Airflow. - Что: - внедрена клауза WITH (appendonly=...) во все DDL; ODS-справочники переведены на AO Row и TRUNCATE+INSERT. - создан скрипт scripts/e2e_etl.sh для полного прогона ETL (DDL + 2 дня данных) через REST API. - исправлены баги типизации (INTEGER[]), именования полей (amount, passenger_id) и удалены фантомные колонки (contact_data). - обновлен e2e-etl-test-protocol.md: добавлен раздел по отладке, чтению логов и перезапуску задач через API. - исправлены pytest-контракты под новую логику загрузки. - Проверка: - успешный прогон `make e2e-smoke` (полный цикл от очистки до витрины).
105 lines
3.8 KiB
Bash
Executable File
105 lines
3.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Скрипт автоматизированного прогона E2E-теста всей цепочки DWH через REST API Airflow.
|
|
# Отрабатывает 2 "учебных дня" для проверки инкрементальной загрузки.
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$ROOT"
|
|
|
|
warn() { echo ":: ${1}" >&2; }
|
|
|
|
# Загружаем переменные из .env, если файл существует, иначе используем дефолты
|
|
if [ -f ".env" ]; then
|
|
export $(grep -v '^#' .env | xargs)
|
|
fi
|
|
|
|
AIRFLOW_USER=${AIRFLOW_USER:-admin}
|
|
AIRFLOW_PASSWORD=${AIRFLOW_PASSWORD:-admin}
|
|
API_URL="http://localhost:8080/api/v1/dags"
|
|
|
|
# Функция для триггера DAG и ожидания его завершения
|
|
trigger_and_wait() {
|
|
local dag_id=$1
|
|
warn "Запуск $dag_id через REST API..."
|
|
|
|
# 0. Unpause DAG
|
|
curl -s -X PATCH "$API_URL/$dag_id" \
|
|
--user "$AIRFLOW_USER:$AIRFLOW_PASSWORD" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"is_paused": false}' > /dev/null
|
|
|
|
# 1. Trigger
|
|
local response
|
|
response=$(curl -s -X POST "$API_URL/$dag_id/dagRuns" \
|
|
--user "$AIRFLOW_USER:$AIRFLOW_PASSWORD" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{}')
|
|
|
|
local run_id
|
|
run_id=$(echo "$response" | grep -o '"dag_run_id": "[^"]*' | cut -d'"' -f4)
|
|
if [ -z "$run_id" ]; then
|
|
echo "Ошибка запуска $dag_id. Ответ API: $response" >&2
|
|
exit 1
|
|
fi
|
|
warn "Успешный триггер. Run ID: $run_id"
|
|
|
|
# 2. Wait
|
|
warn "Ожидание завершения $dag_id..."
|
|
local attempts=0
|
|
local max_attempts=120 # 10 минут (120 * 5 сек)
|
|
|
|
while true; do
|
|
local status
|
|
status=$(curl -s "$API_URL/$dag_id/dagRuns/$run_id" \
|
|
--user "$AIRFLOW_USER:$AIRFLOW_PASSWORD" | grep -o '"state": "[^"]*' | cut -d'"' -f4)
|
|
|
|
if [ "$status" = "success" ]; then
|
|
warn "DAG $dag_id завершен: SUCCESS"
|
|
break
|
|
elif [ "$status" = "failed" ]; then
|
|
echo "DAG $dag_id УПАЛ (FAILED)!" >&2
|
|
exit 1
|
|
elif [ "$status" = "queued" ] || [ "$status" = "running" ] || [ "$status" = "" ]; then
|
|
attempts=$((attempts + 1))
|
|
if [ "$attempts" -ge "$max_attempts" ]; then
|
|
echo "Таймаут ожидания $dag_id!" >&2
|
|
exit 1
|
|
fi
|
|
sleep 5
|
|
else
|
|
echo "Неизвестный статус: $status" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
warn "=== DDL: Создание схем и таблиц ==="
|
|
trigger_and_wait "bookings_stg_ddl"
|
|
trigger_and_wait "bookings_ods_ddl"
|
|
trigger_and_wait "bookings_dds_ddl"
|
|
trigger_and_wait "bookings_dm_ddl"
|
|
|
|
warn "=== DAY 1: Initial Load ==="
|
|
trigger_and_wait "bookings_to_gp_stage"
|
|
trigger_and_wait "bookings_to_gp_ods"
|
|
trigger_and_wait "bookings_to_gp_dds"
|
|
trigger_and_wait "bookings_to_gp_dm"
|
|
|
|
warn "=== DAY 2: Increment ==="
|
|
trigger_and_wait "bookings_to_gp_stage"
|
|
trigger_and_wait "bookings_to_gp_ods"
|
|
trigger_and_wait "bookings_to_gp_dds"
|
|
trigger_and_wait "bookings_to_gp_dm"
|
|
|
|
warn "=== Верификация данных ==="
|
|
# Выполняем простые проверки строк, чтобы убедиться, что данные дошли до витрины
|
|
docker compose -f docker-compose.yml exec greenplum bash -c "su - gpadmin -c \"psql -d gp_dwh -c \\\"
|
|
SELECT 'STG bookings' as layer, COUNT(*) FROM stg.bookings UNION ALL
|
|
SELECT 'ODS bookings', COUNT(*) FROM ods.bookings UNION ALL
|
|
SELECT 'DDS fact', COUNT(*) FROM dds.fact_flight_sales UNION ALL
|
|
SELECT 'DM report', COUNT(*) FROM dm.sales_report;
|
|
\\\"\""
|
|
|
|
warn "E2E ETL тест успешно завершен!"
|