fix(smoke): «ответа нет» ≠ «неверный код» — ретраи и честная формулировка в смоуке периметра МЕРА #3441

Merged
bot-backend merged 2 commits from fix/perimeter-smoke-retry-on-no-response into main 2026-09-11 20:16:28 +00:00
Collaborator

Смоук публичного периметра краснел там, где периметр цел.

Факт (11.09). Прогон perimeter-smoke-mera на голове main a659b187 — failure; упали две последние проверки:

FAIL: trade-in payments/notify — 503 anonymous (… -> got '000', expected 503)
FAIL: trade-in payments/notify — 405 на GET (… -> got '000', expected 405)

000 у curl — ответа не было вовсе, а не «пришёл неверный код». Ручная проверка тех же путей сразу после: 503 и 405 от приложения (server: uvicorn, без заглушки Caddy). Периметр исправен.

Замер причины (11.09, внешний IP): серия к gendsgn.ru/trade-in/api/v1/me без пауз — 5 обрывов из 12; пауза 0.5 с — 6 из 12; пауза 2 с — 0 из 8. С самого хоста Poincare — 0 из 10. Оба домена резолвятся в один адрес, вход общий. Смоук шлёт ~28 запросов подряд и попадает под тот же эффект — падают именно последние проверки списка.

Правка. Общая обёртка curl_try вокруг ВСЕХ запросов скрипта (6 функций-проверок + загрузка HTML лэндинга), а не заплатка у двух строк. «Ответа не было» определяется по коду возврата curl (28/35/52/56/6) — строго эквивалентно http_code=000, но доступно и проверкам, которые http_code не запрашивают. Побочно чинит два ложных диагноза: обрыв в check_redirect_location читался как «Location не тот» (то есть как потеря query!), обрыв при загрузке лэндинга — как «лэндинг сломан».

  • До 3 попыток, паузы 2 с → 4 с; повтор только при «ответа нет». Ответ с неверным кодом для curl успешен и не повторяется никогда — ретраи не могут замаскировать регресс.
  • Отдельная формулировка: FAIL (ответа нет): … вход не отдал ответ после 3 попыток; это НЕ измеренный код ответа, периметр этой проверкой НЕ проверен + итоговая строка БЕЗ ОТВЕТА: N проверок. Прогон красный — мы честно не знаем, цел ли периметр.
  • Пауза 2 с между проверками — наименьшая величина, у которой есть замер (0.5 с измеренно не помогает; промежуточные не мерил никто). Переопределяется: SMOKE_PAUSE=0 SMOKE_ATTEMPTS=1 возвращает прежнее поведение.
  • timeout-minutes: 5 → 10: неотвечающая проверка стоит до 3×15 с плюс паузы, трёх таких хватило бы, чтобы job убили ДО печати FAIL-строк.

Ожидания, коды и список путей не тронуты — в диффе нет ни одного изменённого ожидаемого кода или URL.

Проверка по значению (пара «до/после» ничего не доказывает — сегодня оба прогона зелёные, эффект перемежающийся; поэтому проверки детерминированные):

  • ответа нет вовсе (базовые адреса → 127.0.0.1:9): 38 × FAIL (ответа нет), итог БЕЗ ОТВЕТА: 38, выход 1;
  • обрыв, лечимый повтором (подставной curl роняет каждый нечётный запрос): 44 RETRY, 43/43 PASS, выход 0 — прогон, который краснел бы, зеленеет за счёт второго запроса, а не ослабления;
  • настоящий регресс (подмены: ожидание /me 401→200 и URL /oferta→несуществующий): 2 × FAIL, RETRY-строк ноль, строки БЕЗ ОТВЕТА нет.

Прогоны: прод до правки 43/43 PASS (89 с), после 43/43 PASS, 0 RETRY (160 с). Гейт синтаксиса из ci.yml✓ 30 shell-скриптов; pre-commit чисто.

Что остаётся проверить руками: прогнать perimeter-smoke-mera на ветке через workflow_dispatch — это единственный вантаж, где 000 реально случается. Если бан раннера длится минутами, 3 попытки его не пересидят — но прогон тогда скажет БЕЗ ОТВЕТА: N, а не соврёт про регресс.

Смоук публичного периметра краснел там, где периметр цел. **Факт (11.09).** Прогон `perimeter-smoke-mera` на голове main `a659b187` — failure; упали две последние проверки: ``` FAIL: trade-in payments/notify — 503 anonymous (… -> got '000', expected 503) FAIL: trade-in payments/notify — 405 на GET (… -> got '000', expected 405) ``` `000` у curl — ответа не было вовсе, а не «пришёл неверный код». Ручная проверка тех же путей сразу после: 503 и 405 от приложения (`server: uvicorn`, без заглушки Caddy). Периметр исправен. **Замер причины (11.09, внешний IP):** серия к `gendsgn.ru/trade-in/api/v1/me` без пауз — 5 обрывов из 12; пауза 0.5 с — 6 из 12; пауза 2 с — 0 из 8. С самого хоста Poincare — 0 из 10. Оба домена резолвятся в один адрес, вход общий. Смоук шлёт ~28 запросов подряд и попадает под тот же эффект — падают именно последние проверки списка. **Правка.** Общая обёртка `curl_try` вокруг ВСЕХ запросов скрипта (6 функций-проверок + загрузка HTML лэндинга), а не заплатка у двух строк. «Ответа не было» определяется по коду возврата curl (28/35/52/56/6) — строго эквивалентно `http_code=000`, но доступно и проверкам, которые http_code не запрашивают. Побочно чинит два ложных диагноза: обрыв в `check_redirect_location` читался как «Location не тот» (то есть как потеря query!), обрыв при загрузке лэндинга — как «лэндинг сломан». - До 3 попыток, паузы 2 с → 4 с; повтор **только** при «ответа нет». Ответ с неверным кодом для curl успешен и не повторяется никогда — ретраи не могут замаскировать регресс. - Отдельная формулировка: `FAIL (ответа нет): … вход не отдал ответ после 3 попыток; это НЕ измеренный код ответа, периметр этой проверкой НЕ проверен` + итоговая строка `БЕЗ ОТВЕТА: N проверок`. Прогон красный — мы честно не знаем, цел ли периметр. - Пауза 2 с между проверками — наименьшая величина, у которой есть замер (0.5 с измеренно не помогает; промежуточные не мерил никто). Переопределяется: `SMOKE_PAUSE=0 SMOKE_ATTEMPTS=1` возвращает прежнее поведение. - `timeout-minutes: 5 → 10`: неотвечающая проверка стоит до 3×15 с плюс паузы, трёх таких хватило бы, чтобы job убили ДО печати FAIL-строк. Ожидания, коды и список путей не тронуты — в диффе нет ни одного изменённого ожидаемого кода или URL. **Проверка по значению** (пара «до/после» ничего не доказывает — сегодня оба прогона зелёные, эффект перемежающийся; поэтому проверки детерминированные): - ответа нет вовсе (базовые адреса → `127.0.0.1:9`): 38 × `FAIL (ответа нет)`, итог `БЕЗ ОТВЕТА: 38`, выход 1; - обрыв, лечимый повтором (подставной curl роняет каждый нечётный запрос): 44 RETRY, **43/43 PASS**, выход 0 — прогон, который краснел бы, зеленеет за счёт второго запроса, а не ослабления; - настоящий регресс (подмены: ожидание `/me` 401→200 и URL `/oferta`→несуществующий): 2 × FAIL, **RETRY-строк ноль**, строки `БЕЗ ОТВЕТА` нет. **Прогоны:** прод до правки 43/43 PASS (89 с), после 43/43 PASS, 0 RETRY (160 с). Гейт синтаксиса из `ci.yml` — `✓ 30 shell-скриптов`; pre-commit чисто. **Что остаётся проверить руками:** прогнать `perimeter-smoke-mera` на ветке через `workflow_dispatch` — это единственный вантаж, где `000` реально случается. Если бан раннера длится минутами, 3 попытки его не пересидят — но прогон тогда скажет `БЕЗ ОТВЕТА: N`, а не соврёт про регресс.
bot-backend added 1 commit 2026-09-11 19:31:24 +00:00
fix(smoke): отличать «ответа не было» от «код не тот» в смоуке периметра
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
c0e45b48d3
Прогон perimeter-smoke-mera на голове main (a659b187) покраснел на двух
последних проверках с кодом `000`. `000` у curl — это не «пришёл неверный
код», а «ответа не было вовсе»: периметр был цел, те же пути вручную
отдавали 503 и 405 от приложения (server: uvicorn, не заглушка Caddy).

Замер причины (внешний IP, /trade-in/api/v1/me): серия без пауз — 5 обрывов
из 12, с паузой 0.5 c — 6 из 12, с паузой 2 c — 0 из 8; с самого хоста прода
— 0 из 10. Приложение отвечает, частую серию запросов с одного адреса
отбивает вход. Смоук шлёт 44 запроса подряд и попадает под тот же эффект,
поэтому падают последние проверки списка.

Что сделано (ожидания и список путей НЕ тронуты):
- один общий curl_try, через который идут все запросы смоука. Повтор
  только при «ответа не было» — признак берётся у самого curl (ненулевой
  код возврата), ответ с «не тем» кодом для curl успешен и не повторяется
  никогда, иначе ретрай маскировал бы настоящий регресс;
- отдельная формулировка FAIL (ответа нет) + итоговая строка «БЕЗ ОТВЕТА:
  N проверок» — чтобы читатель красного лога не искал регресс периметра
  там, где измерения не было;
- пауза 2 c между проверками. Наименьшая величина, у которой есть замер:
  0.5 c измеренно не помогает, промежуточные значения не мерил никто;
- timeout-minutes воркфлоу 5 → 10: обычный прогон 89 c → 160 c (замер), а
  неотвечающая проверка стоит до 3×15 c таймаута плюс паузы, и job убивали
  бы до печати FAIL-строк.

Побочно тем же сторожем закрыты места, где обрыв врал диагнозом: в
check_redirect_location он читался как «Location не тот», а обрыв на
загрузке лэндинга — как «сам лэндинг сломан».

Проверка правки: прогон до (89 c, 43/43 PASS) и после (160 c, 43/43 PASS);
подставной curl, роняющий каждый нечётный запрос, — 44 повтора, итог
зелёный; фальсификация с подменённым ожиданием (/me → 200) и неверным URL
(/oferta-net-takogo) — обе строки красные, повторов ноль, выход 1.
Light1YT added 1 commit 2026-09-11 20:07:22 +00:00
fix(smoke): отказ TLS-сертификата — это FAIL, а не «ответа нет»
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
134985a624
Повтор запроса включался на ЛЮБОМ ненулевом rc curl, хотя собственный
комментарий рядом называл сетевой класс (6/28/35/52/56). Протухший, чужой
или самоподписанный сертификат даёт rc=60 (замер: expired.badssl.com,
self-signed.badssl.com, wrong.host.badssl.com) — и измеренный регресс
периметра уезжал в колонку «периметр этой проверкой НЕ проверен», потратив
на детерминированный отказ три попытки и 6 c пауз. Ровно этот отказ и есть
предмет проверки 5b: без site-блока Caddy не выпускает сертификат.

Коды сетевого класса вынесены в NETWORK_RC рядом с комментарием, чтобы
описание и поведение не разъезжались; повтор делается только по ним. rc=7
(соединение отвергнуто) добавлен туда же — ответа при нём тоже нет.
curl_failed печатает rc в обеих ветках: строки RETRY при SMOKE_ATTEMPTS=1
нет вовсе, и «домена нет» (6) было не отличить от «сертификат протух» (60).

timeout-minutes 10 → 40: худший случай (прод не отвечает — мертвы все 43
проверки) = 43 × 53 c ≈ 38 мин, в 10 минут помещалось ~8 мёртвых проверок,
и job убивали ДО печати FAIL-строк и итога — в том самом сценарии, ради
которого правка и делалась.
bot-backend merged commit 09c7bb3ac7 into main 2026-09-11 20:16:28 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3441
No description provided.