test(tradein/auth): флаки-замер «< 500 мс» заменён проверкой механизма — bcrypt обязан исполняться вне потока event loop #3350

Merged
bot-backend merged 2 commits from fix/3343-auth-latency-flaky into main 2026-09-05 18:14:40 +00:00
Collaborator

Closes #3343. Тест из #2712 мерил абсолютные 500 мс wall-clock на shared-раннере и падал под чужой нагрузкой, роняя деплой (03.09 прод остался на старых образах; сегодня та же угроза висела над тремя деплоями пачки).

Вариант 2 из issue — механизм, не время

verify_password_bounded уносит сверку в _VERIFY_POOL.submit(...) → «вынесено из цикла» — дискретный наблюдаемый факт «сверка исполнилась в другом потоке»:

  • стенд-двойник bcrypt пишет threading.get_ident() в verify_threads;
  • вердикт: threading.get_ident() not in verify_threads — поток теста и есть поток цикла;
  • «цикл не заблокирован» — прежний счётчик тиков пробы ≥ 10 (inline-bcrypt: проба почти не успевает; пул: ~100 тиков — запас ×10 в обе стороны, от загрузки раннера зависит пропускная способность, а не порядок);
  • абсолютные худший < 500 мс / медиана < 50 мс сняты как вердикт, печатаются как диагностика в сообщении падения.

Вариант 1 (относительный порог) отвергнут: под чужой сборкой плывёт и baseline. Вариант 3 (reruns) — запрещён самой issue.

Фальсификация

return verify_password(...) прямо в verify_password_bounded (bcrypt синхронно в loop) → AssertionError: сверка пароля исполнилась в потоке событийного цикла — … assert 8387963968 not in {8387963968}. Восстановлено.

Стабильность

5 прогонов подряд под искусственной нагрузкой (12×yes, load average 7.95 → 24.21): все 1 passed за 2.9-3.4 с. Полный файл 48 passed.

Один файл, +35/−10, код приложения не тронут.

Closes #3343. Тест из #2712 мерил абсолютные 500 мс wall-clock на shared-раннере и падал под чужой нагрузкой, роняя деплой (03.09 прод остался на старых образах; сегодня та же угроза висела над тремя деплоями пачки). ## Вариант 2 из issue — механизм, не время `verify_password_bounded` уносит сверку в `_VERIFY_POOL.submit(...)` → «вынесено из цикла» — дискретный наблюдаемый факт «сверка исполнилась в другом потоке»: - стенд-двойник bcrypt пишет `threading.get_ident()` в `verify_threads`; - вердикт: `threading.get_ident() not in verify_threads` — поток теста и есть поток цикла; - «цикл не заблокирован» — прежний счётчик тиков пробы `≥ 10` (inline-bcrypt: проба почти не успевает; пул: ~100 тиков — запас ×10 в обе стороны, от загрузки раннера зависит пропускная способность, а не порядок); - абсолютные `худший < 500 мс` / `медиана < 50 мс` сняты как вердикт, печатаются как диагностика в сообщении падения. Вариант 1 (относительный порог) отвергнут: под чужой сборкой плывёт и baseline. Вариант 3 (reruns) — запрещён самой issue. ## Фальсификация `return verify_password(...)` прямо в `verify_password_bounded` (bcrypt синхронно в loop) → `AssertionError: сверка пароля исполнилась в потоке событийного цикла — … assert 8387963968 not in {8387963968}`. Восстановлено. ## Стабильность 5 прогонов подряд под искусственной нагрузкой (12×`yes`, load average 7.95 → 24.21): все `1 passed` за 2.9-3.4 с. Полный файл `48 passed`. Один файл, +35/−10, код приложения не тронут.
bot-backend added 1 commit 2026-09-05 17:52:05 +00:00
test(auth): проверять вынос bcrypt механизмом, а не секундомером
All checks were successful
CI Trade-In / changes (pull_request) Successful in 15s
CI / changes (pull_request) Successful in 19s
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
CI Trade-In / backend-tests (pull_request) Successful in 5m53s
ecd6d5c91d
`probe_latencies[-1] < 0.5` мерил на общем раннере не свойство кода, а
свободен ли CPU у соседей: 03.09 деплой Trade-In встал на «худший
сторонний запрос 544мс» на коммите, который часом раньше прошёл на
незанятой машине (#3343). Тест из #2712 проверял правильную вещь
неправильной метрикой.

Вердикт теперь дискретный: сверка пароля обязана исполниться НЕ в потоке
событийного цикла (двойник bcrypt пишет `threading.get_ident()`).
Загрузка раннера этого не подделает. Второй половиной остаётся счётчик
тиков пробы: при bcrypt в `async def` цикл стоит всю секунду флуда и
проба не успевает почти никогда, при выносе в пул успевает ~100 —
порог 10 лежит посередине с запасом ×10 в обе стороны. Абсолютные
латентности сохранены в тексте падения как диагностика, но не гейтят.

Фальсификация: возврат `verify_password` из пула прямо в `async def`
роняет тест на новом утверждении («сверка пароля исполнилась в потоке
событийного цикла», `assert 8387963968 not in {8387963968}`), 5 прогонов
подряд под load average 24 — зелёные.

Closes #3343
Light1YT added 1 commit 2026-09-05 18:06:56 +00:00
test(auth): порог пробы по темпу тиков, диагностика в лог CI
All checks were successful
CI Trade-In / changes (pull_request) Successful in 14s
CI / changes (pull_request) Successful in 17s
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
CI Trade-In / backend-tests (pull_request) Successful in 6m17s
cd95222d90
Абсолютное `len(probe_latencies) >= 10` слабело ровно в целевом сценарии:
elapsed под нагрузкой растёт, и заблокированный цикл добирает 10 тиков за
3-5с. Сверяем темп: замер пул 65.5-66.3 тик/с против 1.0 тик/с у bcrypt в
`async def`, порог 8.0 — геометрическая середина, запас x8 в обе стороны.

Тики/медиану/темп печатаем безусловно через capsys.disabled(): у зелёного
теста pytest захваченный вывод не показывает, а запас на общем раннере виден
только когда всё прошло.
bot-backend merged commit 4441e6ccbd into main 2026-09-05 18:14:40 +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#3350
No description provided.