feat(sql): явное управление storage и автоматизированный E2E-тест через REST API
- Зачем: - необходимо визуализировать выбор типа хранения (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` (полный цикл от очистки до витрины).
This commit is contained in:
Executable
+104
@@ -0,0 +1,104 @@
|
||||
#!/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 тест успешно завершен!"
|
||||
+2
-22
@@ -38,30 +38,10 @@ wait_up airflow-scheduler
|
||||
warn "Init demo DB bookings"
|
||||
make bookings-init
|
||||
|
||||
warn "Apply DDL to Greenplum"
|
||||
make ddl-gp
|
||||
|
||||
warn "Run local pytest suite"
|
||||
make test
|
||||
|
||||
warn "Airflow DAG test: csv_to_greenplum"
|
||||
docker compose -f docker-compose.yml exec airflow-webserver airflow dags test csv_to_greenplum 2024-01-01
|
||||
|
||||
warn "Check orders count in Greenplum"
|
||||
ORDERS_COUNT=$(docker compose -f docker-compose.yml exec greenplum bash -lc "su - gpadmin -c \"/usr/local/greenplum-db/bin/psql -t -A -d gp_dwh -c 'SELECT COUNT(*) FROM public.orders;'\"")
|
||||
if [ "${ORDERS_COUNT:-0}" -le 0 ]; then
|
||||
echo "orders table is empty after csv_to_greenplum (COUNT=${ORDERS_COUNT:-0})" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
warn "Airflow DAG test: bookings_to_gp_stage"
|
||||
docker compose -f docker-compose.yml exec airflow-webserver airflow dags test bookings_to_gp_stage 2024-01-01
|
||||
|
||||
warn "Check stg.bookings count in Greenplum"
|
||||
BOOKINGS_COUNT=$(docker compose -f docker-compose.yml exec greenplum bash -lc "su - gpadmin -c \"/usr/local/greenplum-db/bin/psql -t -A -d gp_dwh -c 'SELECT COUNT(*) FROM stg.bookings;'\"")
|
||||
if [ "${BOOKINGS_COUNT:-0}" -le 0 ]; then
|
||||
echo "stg.bookings is empty after bookings_to_gp_stage (COUNT=${BOOKINGS_COUNT:-0})" >&2
|
||||
exit 1
|
||||
fi
|
||||
warn "Start full E2E ETL test via REST API"
|
||||
make e2e-etl
|
||||
|
||||
warn "Smoke test completed successfully"
|
||||
|
||||
Reference in New Issue
Block a user