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)" + )