Зачем: убрать механические повторы в итогах и избежать ложных расхождений при проверке графика Superset. Что: уточнены правила итоговых списков и название задания о проверках заказов. В разборе событий разделены учет повторных доставок и выбор событий ODS для сравнения. Первый график Superset и проверочный SQL используют весь период витрины. Проверка: git diff --check, локальные ссылки и переходы к измененным заголовкам прошли. Блоки кода и таблицы требований сохранены. Согласованность периода графика с SQL проверена по тексту; повторный проход через интерфейс Superset не выполнялся.
149 lines
9.5 KiB
Markdown
149 lines
9.5 KiB
Markdown
# Научите проверки замечать ошибки в заказах
|
||
|
||
**Чему учит:** написать проверки заказов до изменения их обработки
|
||
и убедиться на намеренной ошибке, что проверки ее замечают.
|
||
|
||
## Сначала прочитать
|
||
|
||
- [модель заказа DDS](../architecture/dds/order.md);
|
||
- [дневной конвейер](../architecture/etl.md#объём-прогона);
|
||
- [правила проверок](../architecture/testing.md#проверка-которая-не-умеет-краснеть-бесполезна).
|
||
|
||
Посмотрите также на устройство одной проверки в [`sql/dq/`](../../sql/dq/)
|
||
и на ее включение в [`dq_check`](../../dags/dq_check.py). Готового ответа для
|
||
двух проверок из этого задания в репозитории нет.
|
||
|
||
## Что предстоит сделать
|
||
|
||
Добавьте две проверки и включите их в даг:
|
||
|
||
- `sql/dq/order_items_total.sql` проверяет сумму позиций заказа;
|
||
- `sql/dq/orders_vs_ods.sql` сверяет DDS с его источником;
|
||
- `dags/dq_check.py` запускает обе проверки, публикует их строки в
|
||
`dm.dq_summary` и создает для них отдельные задачи.
|
||
|
||
Разбор ODS и сборку DDS вы перепишете в следующих заданиях. Здесь сохраните
|
||
их, даги загрузки, DDL, таблицы-доноры, Kafka и службы как есть.
|
||
|
||
Перед опытами остановите автоматическое движение мира: поставьте `world_live`
|
||
на паузу и дождитесь завершения активных прогонов. До завершения опытов не
|
||
запускайте руками `world_next_day`, `world_live_day`, `world_initialize` и
|
||
`world_recreate`. Это условие стабильного модельного времени: одной паузы
|
||
`world_live` для него мало.
|
||
|
||
## Что будут проверять ваши запросы
|
||
|
||
Чтобы `dq_check` мог запустить проверку, ее запрос возвращает по одной строке
|
||
на день с колонками `data_date`, `reference_rows`, `actual_rows`,
|
||
`failed_rows`, `diagnostics`. Даг сохраняет в `dm.dq_summary` только счетчики,
|
||
статус и координаты запуска. Примеры из `diagnostics` ищите в
|
||
журнале упавшей задачи Airflow. Ограничьте их тем же малым числом, что и
|
||
существующие запросы в `sql/dq/`.
|
||
|
||
### Сумма позиций
|
||
|
||
Имя проверки - `order_items_total`. Для каждого заказа из `dds.order_v`
|
||
посчитайте сумму `item_price * item_quantity` и сравните ее с `items_total`.
|
||
`reference_rows` считает ключи со стороны `items_total`, `actual_rows` - ключи
|
||
со стороны вычисленной суммы, а `failed_rows` - несовпавшие значения. В
|
||
исправном наборе первые два счетчика равны; несовпадения сумм считает
|
||
`failed_rows`. Для диагностики достаточно `order_id`, ожидаемой суммы и
|
||
суммы позиций.
|
||
|
||
Проверка относится к типизированному разбору массива `items` на границе
|
||
ODS -> DDS. Скидку, доставку и равенство `total` здесь проверять не нужно.
|
||
|
||
### DDS против ODS
|
||
|
||
Имя проверки - `orders_vs_ods`. Источник модели DDS здесь - текущее состояние
|
||
`ods.order_v`. С ним вы и сравниваете DDS; обращаться к внутреннему состоянию
|
||
генератора для этой проверки не нужно.
|
||
Одним запросом соберите обе стороны на зерне дня заказа и сравните:
|
||
|
||
- число заказов;
|
||
- максимальный `updated_at`.
|
||
|
||
День ODS вычисляется в `Europe/Samara`, как `order_date` в DDS. Область
|
||
проверки - объединение дней обеих сторон, включая день, который полностью
|
||
пропал из одного слоя. Перед сравнением приведите времена к одному часовому
|
||
поясу. `reference_rows` - число заказов ODS, `actual_rows` - число заказов DDS,
|
||
а `failed_rows` равен единице для дня, где расходится хотя бы одно значение.
|
||
|
||
Обе стороны распределены по `order_id`. Дневные агрегаты соедините через
|
||
`GLOBAL FULL OUTER JOIN` с `join_use_nulls = 1`, как в существующих сверках.
|
||
|
||
Если разбор ODS и сборка DDS одинаково потеряют строку, слои останутся равны
|
||
по этой проверке. Общая потеря при разборе проявится в отдельном балансе по
|
||
`_load_id` из [следующего задания](5-2-order-parsing.md).
|
||
|
||
## Получите красный и зеленый результаты
|
||
|
||
Сначала запустите `dq_check` на нетронутых данных. Обе новые задачи должны
|
||
завершиться успешно, а в `dm.dq_summary_v` у обоих имен не должно быть
|
||
нарушенных дней.
|
||
|
||
Выберите оплаченный заказ из последнего слепка с непустым массивом позиций и
|
||
сохраните его `order_id` и `order_date`. Подключитесь к первой ноде из DBeaver
|
||
по [инструкции из README](../../README.md#подключиться-из-dbeaver) под
|
||
пользователем `etl`. Прибавьте одну копейку к `items_total` этого заказа в
|
||
локальной таблице `dds.order_rep` на всем кластере:
|
||
|
||
```sql
|
||
ALTER TABLE dds.order_rep ON CLUSTER clickstream_cluster
|
||
UPDATE items_total = items_total + toDecimal64(0.01, 2)
|
||
WHERE order_id = '<order_id>'
|
||
SETTINGS mutations_sync = 2;
|
||
```
|
||
|
||
Затем запустите только `dq_check`. Задача `order_items_total` должна
|
||
завершиться ошибкой, а ее журнал - назвать выбранный заказ.
|
||
`purchase_vs_orders_vs_dds` тоже даст красный результат: сохраненная витрина
|
||
содержит прежний `items_total`.
|
||
|
||
Запустите `etl_pipeline`. Текущее окно заказов будет заново собрано из ODS, и
|
||
исходная строка вернется в DDS. Повторный `dq_check` должен дать зеленый
|
||
результат для нового сторожа.
|
||
|
||
Теперь удалите тот же заказ из `dds.order_rep` на всем кластере:
|
||
|
||
```sql
|
||
ALTER TABLE dds.order_rep ON CLUSTER clickstream_cluster
|
||
DELETE WHERE order_id = '<order_id>'
|
||
SETTINGS mutations_sync = 2;
|
||
```
|
||
|
||
До восстановления снова запустите только `dq_check`. Красный результат нужен
|
||
для `orders_vs_ods`, `purchase_vs_orders_vs_dds` и `revenue_daily_vs_dds` на
|
||
дне оплаченного заказа. Затем запустите `etl_pipeline` и повторите проверку.
|
||
Если `orders_vs_ods` не обнаружил удаление, исправьте запрос до следующего
|
||
задания.
|
||
|
||
## Как проверить себя
|
||
|
||
В `dm.dq_summary_v` должны быть зелены обе новые и пять прежних проверок:
|
||
|
||
```sql
|
||
SELECT check_name, sum(failed_rows) AS failed_rows, groupUniqArray(status)
|
||
FROM dm.dq_summary_v
|
||
GROUP BY check_name
|
||
ORDER BY check_name;
|
||
```
|
||
|
||
Проверка суммы должна охватывать каждый день `dds.order_v`. Сверка слоев
|
||
должна охватывать объединение дней ODS и DDS, включая дни только одной стороны.
|
||
|
||
## Завершите опыт
|
||
|
||
После восстановления заказа и успешного `dq_check` можно закончить работу или
|
||
перейти к разбору заказов, оставив модельное время на паузе.
|
||
|
||
## Что должно получиться
|
||
|
||
- [ ] две новые проверки запускаются в `dq_check`, а их дневные результаты
|
||
появляются в `dm.dq_summary`;
|
||
- [ ] вы увидели ошибку каждой проверки после соответствующего изменения
|
||
заказа, а в журнале нашли примеры заказов с расхождениями;
|
||
- [ ] вы восстановили заказ через `etl_pipeline` после обоих опытов;
|
||
- [ ] `order_items_total`, `orders_vs_ods` и пять прежних проверок успешны;
|
||
- [ ] разбор ODS и сборка DDS остались прежними.
|