fix(stand): находки ревью — диагноз не утверждает причину, комментарии не врут
Зачем Холодное ревью нашло три места, где написанное сильнее сделанного. Что - Непустая таблица брака больше не выдаётся за доказательство сломанного разбора. Модельного дня у брака нет, обрамить его нечем, и строки прежних уроков лежат в нём месяц: после первого же урока с мусором проверка давала бы неверный диагноз навсегда. Теперь она даёт признак, по которому причину отличают, — сошлась недостача с числом брака или нет. - Комментарий у world-init обещал, что расхождение числа дней с STARTING_DAYS поймают счётчики. Это неправда в одну сторону: лишний день ложится за рамкой дат описи. Обещание убрано, дыра названа. - Довод «даг next_day этапа 5 продолжит ось» опирался на несуществующий этап; заменён настоящей причиной — заливка замыкает цепь разовых служб. - Потолок ожидания 300 с получил обоснование замером с кратностью, а сам скрипт — честную оговорку: его обещание работает на пустом стенде, на живом ждать нечего. - Третья, пропущенная ссылка на снятый порог скорости дня убрана из спеки. - Даты замеров в карте целей разведены: #42 менял три цели, а не шесть. Проверка Обе ветви диагноза сняты заново на живом стенде: без брака — «не доехали», с браком — признак различения. Стенд восстановлен, все 9 проверок зелёные, брака 0. make lint, typecheck, config-test, test (407 тестов) зелёные. Ссылка: #42
This commit is contained in:
+6
-7
@@ -251,8 +251,9 @@ services:
|
|||||||
# Что это за дни и каким мир обязан выйти — data/world-inventory.json.
|
# Что это за дни и каким мир обязан выйти — data/world-inventory.json.
|
||||||
#
|
#
|
||||||
# Число дней стоит здесь числом: YAML не читает Python, и одно из двух мест
|
# Число дней стоит здесь числом: YAML не читает Python, и одно из двух мест
|
||||||
# (второе — `STARTING_DAYS` в inventory.py) лишнее по построению. Разъедутся
|
# (второе — `STARTING_DAYS` в inventory.py) лишнее по построению. Правя одно,
|
||||||
# они — покраснеют счётчики `make check-clickhouse`.
|
# правьте второе — на страже тут никто не стоит: залей эта служба лишний
|
||||||
|
# день, он лёг бы за рамкой дат описи и остался бы незамеченным.
|
||||||
#
|
#
|
||||||
# Повторный `make up` заливает мир заново, и это не оплошность: `WatchID` у
|
# Повторный `make up` заливает мир заново, и это не оплошность: `WatchID` у
|
||||||
# событий те же, ReplacingMergeTree схлопнет повтор в ODS. Сырьё в STG при
|
# событий те же, ReplacingMergeTree схлопнет повтор в ODS. Сырьё в STG при
|
||||||
@@ -309,11 +310,9 @@ services:
|
|||||||
# упавшим для --wait.
|
# упавшим для --wait.
|
||||||
clickhouse-init:
|
clickhouse-init:
|
||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
# По той же причине — и ещё по одной. Заливка стартового мира идёт
|
# По той же причине. Заливка стартового мира идёт последней в цепи
|
||||||
# последней в цепи разовых служб, и зависимого ей взять больше негде.
|
# разовых служб, и зависимого ей взять больше негде: долгоживущие службы
|
||||||
# Заодно это правда про порядок: даг `next_day` этапа 5 продолжает ось с
|
# данных не ждут, а `airflow-init` эту цепь и так замыкает.
|
||||||
# того дня, на котором заливка остановилась, — Airflow приходит в мир,
|
|
||||||
# который уже есть.
|
|
||||||
world-init:
|
world-init:
|
||||||
condition: service_completed_successfully
|
condition: service_completed_successfully
|
||||||
entrypoint: ["/bin/bash"]
|
entrypoint: ["/bin/bash"]
|
||||||
|
|||||||
@@ -29,8 +29,9 @@
|
|||||||
|
|
||||||
## Карта целей
|
## Карта целей
|
||||||
|
|
||||||
Стенд нужен трём целям из семи. Цена — замер 7 августа 2026 года, см. «Что
|
Стенд нужен трём целям из семи. Цена — замер, см. «Что проверено»: `make test`,
|
||||||
проверено».
|
`make smoke` и `make check-clickhouse` перемерены 7 августа 2026 года, их #42
|
||||||
|
менял; остальные три стоят с замера 6 августа.
|
||||||
|
|
||||||
| Цель | Что утверждает | Стенд | Цена |
|
| Цель | Что утверждает | Стенд | Цена |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
@@ -204,11 +205,13 @@ Kafka → STG → ODS, и живёт он в `make check-clickhouse`.
|
|||||||
Что новая проверка **умеет краснеть**, снято двумя поломками того же дня, и
|
Что новая проверка **умеет краснеть**, снято двумя поломками того же дня, и
|
||||||
проверялись обе ветви её диагноза. Снесли партицию `2026-06-03` в
|
проверялись обе ветви её диагноза. Снесли партицию `2026-06-03` в
|
||||||
`ods.event_rep` — проверка покраснела, показала недостающий день и назвала
|
`ods.event_rep` — проверка покраснела, показала недостающий день и назвала
|
||||||
адрес: «события не доехали до ODS, начните с чтеца топика». Затем положили в
|
адрес: «таблица брака пуста, события не доехали до ODS, начните с чтеца
|
||||||
сырьё заведомо негодную строку — брак появился, и проверка сменила диагноз на
|
топика». Затем положили в сырьё заведомо негодную строку — брак появился, и
|
||||||
«сломан разбор, начните с `ods.event_errors_dist`». Строки опыта убраны, день
|
диагноз сменился на второй, с признаком для различения: «сойдётся недостача с
|
||||||
переигран `make generate-batch GENERATOR_DAY=2`; счёт вернулся к 401 185, и
|
числом брака — сломан разбор, не сойдётся — брак от прежних опытов». Строки
|
||||||
это заодно показало дедупликацию: повтор дня не удвоил счёт под `FINAL`.
|
опыта убраны, день переигран `make generate-batch GENERATOR_DAY=2`; счёт
|
||||||
|
вернулся к 401 185, и это заодно показало дедупликацию: повтор дня не удвоил
|
||||||
|
счёт под `FINAL`.
|
||||||
|
|
||||||
Замеры 6 августа 2026 года, стенд поднят заранее. Время взято по `time` и
|
Замеры 6 августа 2026 года, стенд поднят заранее. Время взято по `time` и
|
||||||
совпадает с тем, что цель печатает сама. Оно плавает от прогона к прогону:
|
совпадает с тем, что цель печатает сама. Оно плавает от прогона к прогону:
|
||||||
|
|||||||
@@ -888,9 +888,8 @@
|
|||||||
пока число правдоподобно, само по себе оно ничего не сторожит, и
|
пока число правдоподобно, само по себе оно ничего не сторожит, и
|
||||||
подгонять поведение под его край не надо. Настоящий предел здесь другой и
|
подгонять поведение под его край не надо. Настоящий предел здесь другой и
|
||||||
считается в другой валюте — дневной бюджет событий: события корзины
|
считается в другой валюте — дневной бюджет событий: события корзины
|
||||||
входят в те самые «около 50 тыс.», на которых стоят опись (#42) и
|
входят в те самые «около 50 тыс.», на которых стоит опись (#42). Упрётся
|
||||||
порог скорости дня. Упрётся будущая правка — двигать надо бюджет и его
|
будущая правка — двигать надо бюджет и его причины, а не долю.
|
||||||
причины, а не долю.
|
|
||||||
|
|
||||||
Решено при исполнении #41 (2026-08-07). Первые два пункта — решения владельца
|
Решено при исполнении #41 (2026-08-07). Первые два пункта — решения владельца
|
||||||
грилингом 7 августа 2026 года, внесённые как есть; остальные приняты при
|
грилингом 7 августа 2026 года, внесённые как есть; остальные приняты при
|
||||||
|
|||||||
@@ -94,6 +94,11 @@ on_signal() {
|
|||||||
# Таблица брака здесь не второе утверждение, а объяснение первого. Утверждай мы
|
# Таблица брака здесь не второе утверждение, а объяснение первого. Утверждай мы
|
||||||
# «брака нет», проверка краснела бы навсегда после первого же урока, где менти
|
# «брака нет», проверка краснела бы навсегда после первого же урока, где менти
|
||||||
# нарочно отправил в топик мусор, — и краснела бы не о том.
|
# нарочно отправил в топик мусор, — и краснела бы не о том.
|
||||||
|
#
|
||||||
|
# По той же причине непустой брак сам по себе ничего не доказывает: модельного
|
||||||
|
# дня у брака нет, обрамить его нечем, и строки прежних уроков лежат в нём
|
||||||
|
# месяц. Поэтому объяснение не утверждает причину, а даёт признак, по которому
|
||||||
|
# её отличают: сошлась недостача с числом брака — разбор, не сошлась — доставка.
|
||||||
check_starting_world() {
|
check_starting_world() {
|
||||||
local expected actual broken first_date last_date
|
local expected actual broken first_date last_date
|
||||||
|
|
||||||
@@ -122,11 +127,11 @@ check_starting_world() {
|
|||||||
FORMAT TSV")"
|
FORMAT TSV")"
|
||||||
printf 'Опись мира ожидает (дата, событий):\n%s\n' "$expected" >&2
|
printf 'Опись мира ожидает (дата, событий):\n%s\n' "$expected" >&2
|
||||||
printf 'В ods.event лежит:\n%s\n' "${actual:-— ничего —}" >&2
|
printf 'В ods.event лежит:\n%s\n' "${actual:-— ничего —}" >&2
|
||||||
if [[ -n "$broken" ]]; then
|
if [[ -z "$broken" ]]; then
|
||||||
printf 'В ods.event_errors по классам брака:\n%s\n' "$broken" >&2
|
|
||||||
fail 'счёт разошёлся с описью, и в таблице брака есть строки: сломан разбор — начните с ods.event_errors_dist и матвью ods.event_mv'
|
|
||||||
fi
|
|
||||||
fail 'счёт разошёлся с описью, а таблица брака пуста: события не доехали до ODS — начните с чтеца топика stg.hits_raw_kafka и матвью приёма stg.hits_raw_mv'
|
fail 'счёт разошёлся с описью, а таблица брака пуста: события не доехали до ODS — начните с чтеца топика stg.hits_raw_kafka и матвью приёма stg.hits_raw_mv'
|
||||||
|
fi
|
||||||
|
printf 'В ods.event_errors по классам брака:\n%s\n' "$broken" >&2
|
||||||
|
fail 'счёт разошёлся с описью, и в таблице брака есть строки. Сойдётся недостача с их числом — сломан разбор, смотрите матвью ods.event_mv; не сойдётся — брак остался от прежних опытов, а события не доехали: смотрите чтеца топика stg.hits_raw_kafka'
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_ddl_queue_completed() {
|
assert_ddl_queue_completed() {
|
||||||
|
|||||||
@@ -15,10 +15,19 @@
|
|||||||
# брака. Сломайся разбор — события уедут в `*_errors`, сумма сойдётся, ожидание
|
# брака. Сломайся разбор — события уедут в `*_errors`, сумма сойдётся, ожидание
|
||||||
# кончится, и поломку назовёт `make check-clickhouse`. Ждать здесь одних годных
|
# кончится, и поломку назовёт `make check-clickhouse`. Ждать здесь одних годных
|
||||||
# событий значило бы висеть пять минут вместо внятного ответа.
|
# событий значило бы висеть пять минут вместо внятного ответа.
|
||||||
|
#
|
||||||
|
# Обещание у скрипта ровно одно и только на пустом стенде: «мир доехал». На
|
||||||
|
# живом стенде мир уже лежит в ODS, условие истинно на первой же попытке, и
|
||||||
|
# ждать тут нечего — повторная заливка ничего не добавляет, её схлопнет
|
||||||
|
# дедупликация. Утверждать после неё «доехала именно эта заливка» скрипт не
|
||||||
|
# может и не пытается: у события нет отметки, которым прогоном оно приехало.
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
|
||||||
readonly ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
readonly ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
readonly INVENTORY="${ROOT_DIR}/data/world-inventory.json"
|
readonly INVENTORY="${ROOT_DIR}/data/world-inventory.json"
|
||||||
|
# Потолок ожидания — 300 с: больше чем вдесятеро от замеренных 22,5 с, за
|
||||||
|
# которые заливка отдаёт восемь дней в Kafka (карта проверок, «Что проверено»).
|
||||||
|
# Это не бюджет, а предел, за которым ждать бессмысленно и надо читать журнал.
|
||||||
readonly ATTEMPTS=100
|
readonly ATTEMPTS=100
|
||||||
readonly PAUSE_SECONDS=3
|
readonly PAUSE_SECONDS=3
|
||||||
# Как часто отчитываться о ходе: молчащая минуту команда выглядит зависшей.
|
# Как часто отчитываться о ходе: молчащая минуту команда выглядит зависшей.
|
||||||
|
|||||||
Reference in New Issue
Block a user