From b2ff92afbbaecba98f46c67018d0e5886984500d Mon Sep 17 00:00:00 2001 From: bot-backend Date: Thu, 17 Sep 2026 13:09:44 +0500 Subject: [PATCH] =?UTF-8?q?beat:=20=D0=BA=D0=B0=D1=82=D0=B0=D0=BB=D0=BE?= =?UTF-8?q?=D0=B3=20DOM.=D0=A0=D0=A4=20=D0=B2=D1=8B=D0=BA=D0=BB=D1=8E?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=20=D0=B8=D0=B7-=D0=B7=D0=B0=20=D0=B1=D0=BB?= =?UTF-8?q?=D0=BE=D0=BA=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B8=20StormWall,?= =?UTF-8?q?=20=D0=B0=20=D0=BD=D0=B5=20=C2=AB=D0=B4=D0=BE=20cooldown=C2=BB?= =?UTF-8?q?=20(#2443)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Комментарии у закомментированных записей scrape-kn-catalog-objects-weekly и scrape-kn-catalog-flats-weekly обещали «возврат после cooldown 24-48h (проверить через targeted test)». Тест проведён 20.08, 27.08 и 01.09: кулдауна нет (kn-прогоны 29-33 шли в июне, уже после «бана» 24.05), с 01.09 наш.дом.рф за StormWall отдаёт IP Poincare «Доступ заблокирован [403]». Комментарии теперь называют факт и условие включения (прокси и принятый kn-прогон, #3307). Решение больше не живёт только в комментарии: тест проверяет по значению (по task, не по ключу), что ни одна из двух задач не попала в собранное beat-расписание, и в тексте падения ведёт в #3307. Co-Authored-By: Claude Opus 5 --- backend/app/workers/beat_schedule.py | 18 +++++---- .../test_beat_schedule_domrf_catalog.py | 38 +++++++++++++++++++ 2 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 backend/tests/workers/test_beat_schedule_domrf_catalog.py diff --git a/backend/app/workers/beat_schedule.py b/backend/app/workers/beat_schedule.py index b0cce749..35ebe25f 100644 --- a/backend/app/workers/beat_schedule.py +++ b/backend/app/workers/beat_schedule.py @@ -346,11 +346,13 @@ def build_beat_schedule() -> dict: # Вторник 04:00 МСК (crontab в МСК, #1233). batch 300/run → 1532 объекта # за ~5 недель полного обновления. # - # DISABLED 2026-05-24: DOM.РФ WAF дал hard-ban на VPS IP после серии failed - # extras-сессий (run 26/27/28). Catalog SSR использует тот же BrowserSession - # + те же /сервисы/* paths → следующий beat-tick (вт 26.05 04:00 МСК) насыпет - # 300 failed SSR fetches и углубит WAF reputation penalty. Возврат после - # cooldown 24-48h (проверить через targeted test). + # DISABLED с 2026-05-24. Исходная причина «hard-ban, cooldown 24-48h» оказалась + # догадкой: kn-прогоны 29-33 (03.06-28.06) шли успешно уже после 24.05. Зонды + # 20.08, 27.08 и 01.09 (#2443, #3307): наш.дом.рф стоит за StormWall (куки + # spid/spjs/spsc), IP Poincare получает «Доступ заблокирован [403]» — это + # блокировка, ожидание её не снимает. Каталог не собирался с 19.05. + # Включать только после решения по прокси и принятого по числу строк + # kn-прогона (#3307). Выключенность держит test_beat_schedule_domrf_catalog.py. # schedule["scrape-kn-catalog-objects-weekly"] = { # "task": "tasks.scrape_kn_catalog_objects.scrape_kn_catalog_objects", # "schedule": _parse_cron("0 4 * * 2"), # вторник 04:00 МСК @@ -362,11 +364,11 @@ def build_beat_schedule() -> dict: # (цена/статус/отделка/потолки/дата обновления) + plan-изображения квартир из # SSR-страниц каталога. Селектит domrf_kn_flats WHERE catalog_url_hash IS NOT NULL. # - # DISABLED (как и object-level выше): 1) тот же DOM.РФ WAF hard-ban на VPS IP — - # каталог-квартиры используют тот же BrowserSession + /сервисы/* paths. + # DISABLED (как и object-level выше): 1) та же блокировка StormWall на IP + # Poincare — каталог-квартиры используют тот же BrowserSession + /сервисы/* paths. # 2) Sequencing: пока #2442 Task 1 (elemId → catalog_url_hash) не задеплоен и # свежий kn-sweep не наполнил hash, SELECT вернёт 0 строк — включать смысла нет. - # Возврат после WAF-cooldown + первого kn-sweep с hash (проверить targeted-тестом). + # Включать после прокси и принятого kn-прогона с hash (#3307), не по таймеру. # Разнести по времени с object-scrape (вт 04:00), чтобы не двоить WAF-нагрузку — # напр. четверг 04:00 МСК. # schedule["scrape-kn-catalog-flats-weekly"] = { diff --git a/backend/tests/workers/test_beat_schedule_domrf_catalog.py b/backend/tests/workers/test_beat_schedule_domrf_catalog.py new file mode 100644 index 00000000..d2a2b954 --- /dev/null +++ b/backend/tests/workers/test_beat_schedule_domrf_catalog.py @@ -0,0 +1,38 @@ +"""#2443: каталожные DOM.РФ-скраперы не должны попасть в beat, пока стоит блокировка. + +С 01.09 наш.дом.рф за StormWall отдаёт IP Poincare «Доступ заблокирован [403]». +Решение «выключено до прокси» раньше жило только в комментарии с обещанием +«cooldown 24-48h» и месяцами читалось как таймер. Здесь оно проверяется по +значению: включить запись = осознанно поправить этот тест после #3307. +""" + +from __future__ import annotations + +from unittest.mock import patch + +_BLOCKED_TASKS = ( + "tasks.scrape_kn_catalog_objects.scrape_kn_catalog_objects", + "tasks.scrape_kn_catalog_flats.scrape_kn_catalog_flats", +) + + +def test_domrf_catalog_scrapers_not_scheduled_while_stormwall_blocks() -> None: + from app.workers import beat_schedule + + with patch.object(beat_schedule, "_build_beat_schedule_from_db", return_value={}): + schedule = beat_schedule.build_beat_schedule() + + # Контроль: статическая часть расписания реально построилась, иначе + # отсутствие задач ниже было бы зелёным по построению. + assert schedule["refresh-quarter-price-index"]["task"] == ( + "tasks.refresh_quarter_price_index.refresh_quarter_price_index_chain" + ) + + # По task, а не по ключу: переименованный ключ не должен обойти проверку. + scheduled = { + name: entry["task"] for name, entry in schedule.items() if entry["task"] in _BLOCKED_TASKS + } + assert scheduled == {}, ( + f"каталог DOM.РФ включён в beat: {scheduled}. IP Poincare заблокирован " + "StormWall (#2443); включать только после прокси и принятого kn-прогона (#3307)" + )