fix(ptica): download_binary переживает транзиентный ответ, как и get_json (#2464) #2999

Merged
bot-backend merged 1 commit from fix/2464-download-binary-retry into main 2026-08-20 18:24:44 +00:00
Collaborator

Дефект

get_json и download_binary живут в одном классе, ходят через один браузерный контекст и попадают под один и тот же WAF. Но:

429 / 5xx / обрыв
get_json до 5 попыток, backoff 1-2-4-8с
download_binary сразу RuntimeError

Один 429 ронял загрузку картинки насовсем. Вызывающий (download_plan_image, download_photos) ловит исключение и пишет в лог «не удалось» — неотличимо от «файла нет».

Правильный образец лежал в этом же классе, двадцатью строками выше.

Правка

Ретраи по образцу get_json. Непереходные коды (403, 404) поднимаются сразу, без ожидания: повтор их не изменит, а лишний стук под WAF вредит.

Разбор статуса вынесен за семафор — sleep не должен держать слот, иначе backoff одной картинки тормозит остальные.

Как проверено

  • Двусторонне: против origin/main транзиентные тесты красные с конкретным значением — вместо байтов получили RuntimeError('binary http 429: rate limited'); попыток=1. Ни одного ImportError/TypeError.
  • Контроль от «ретраит, но долбит без пауз»: проверяется, что задержки растут как [1, 2]. Без него цикл без sleep выглядел бы в тесте так же, как с ним, — а под WAF разница решающая.
  • Контроли от переусердствования, зелёные с обеих сторон: 403 не повторяется (1 запрос), 404 не повторяется, исчерпание пяти попыток даёт честную ошибку exhausted, а не пустые байты, успех с первой попытки не порождает лишних запросов и пауз.
  • Обрыв соединения тоже ретраится — зеркалит ветку except в get_json.
  • Тесты гоняют настоящий download_binary на двойнике Playwright-контекста, а не проверяют текст функции.
  • pytest backend/tests/services/scrapers/ — 340 passed, 6 skipped.

Часть эпика #2464.

## Дефект `get_json` и `download_binary` живут в одном классе, ходят через один браузерный контекст и попадают под один и тот же WAF. Но: | | 429 / 5xx / обрыв | |---|---| | `get_json` | до 5 попыток, backoff 1-2-4-8с | | `download_binary` | **сразу RuntimeError** | Один 429 ронял загрузку картинки насовсем. Вызывающий (`download_plan_image`, `download_photos`) ловит исключение и пишет в лог «не удалось» — неотличимо от «файла нет». Правильный образец лежал в этом же классе, двадцатью строками выше. ## Правка Ретраи по образцу `get_json`. Непереходные коды (403, 404) поднимаются **сразу**, без ожидания: повтор их не изменит, а лишний стук под WAF вредит. Разбор статуса вынесен **за** семафор — `sleep` не должен держать слот, иначе backoff одной картинки тормозит остальные. ## Как проверено - **Двусторонне:** против `origin/main` транзиентные тесты красные с конкретным значением — `вместо байтов получили RuntimeError('binary http 429: rate limited'); попыток=1`. Ни одного ImportError/TypeError. - **Контроль от «ретраит, но долбит без пауз»:** проверяется, что задержки растут как `[1, 2]`. Без него цикл без `sleep` выглядел бы в тесте так же, как с ним, — а под WAF разница решающая. - **Контроли от переусердствования, зелёные с обеих сторон:** 403 не повторяется (1 запрос), 404 не повторяется, исчерпание пяти попыток даёт честную ошибку `exhausted`, а не пустые байты, успех с первой попытки не порождает лишних запросов и пауз. - **Обрыв соединения** тоже ретраится — зеркалит ветку `except` в `get_json`. - Тесты гоняют настоящий `download_binary` на двойнике Playwright-контекста, а не проверяют текст функции. - `pytest backend/tests/services/scrapers/` — 340 passed, 6 skipped. Часть эпика #2464.
bot-backend added 1 commit 2026-08-20 17:57:52 +00:00
fix(ptica): download_binary переживает транзиентный ответ, как и get_json (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 3m0s
CI / backend-tests (pull_request) Successful in 17m31s
f72a08eb80
Обе функции ходят через один браузерный контекст, под один и тот же WAF.
`get_json` держит до пяти попыток с экспоненциальным backoff на 429/5xx/0,
а `download_binary` не имел ретраев вовсе: один 429 ронял загрузку
картинки насовсем, и вызывающий (`download_plan_image`, `download_photos`)
писал в лог «не удалось» — неотличимо от «файла нет».

Правильный образец лежал в этом же классе, двадцатью строками выше.

Непереходные коды (403, 404) поднимаются сразу, без ожидания: повтор их
не изменит, а лишний стук под WAF вредит. Разбор статуса вынесен ЗА
семафор — sleep не должен держать слот.

Двусторонне: против origin/main транзиентные тесты красные с конкретным
значением («вместо байтов получили RuntimeError('binary http 429…');
попыток=1»), ни одного ImportError/TypeError.

Контроли зелёные с обеих сторон: 403 и 404 не повторяются, исчерпание
попыток даёт честную ошибку, а не пустые байты, успех с первой попытки не
порождает лишних запросов.

Отдельный контроль на паузы: без него «ретраит» и «долбит без пауз»
неотличимы в тесте, а под WAF разница между ними решающая — проверяется,
что задержки растут как 1, 2 секунды.

pytest backend/tests/services/scrapers/ — 340 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bot-backend merged commit 3ecd7cffc8 into main 2026-08-20 18:24:44 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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#2999
No description provided.