fix(nspd_geo): split zombie thresholds running=6m / paused=30m (#1215) #1254

Merged
bot-backend merged 1 commit from fix/nspd-zombie-threshold-vs-waf-backoff into main 2026-06-13 05:37:09 +00:00
Collaborator

Summary

Closes #1215 (P2 concurrency, prod). cleanup_zombies с порогом 2 мин ре-enqueue'ил живой WAF-job worker'а → дубль NSPD-запросов углублял бан.

Bug

nspd_geo worker в WAF-ветке: wait = 30 * min(consecutive_waf, 8) = до 240с, heartbeat коммитится ДО time.sleep(wait). cleanup_zombies бежит каждую минуту с порогом INTERVAL '2 minutes' для status IN ('running', 'paused'). При consecutive_waf >= 5 (sleep 150–240с > 120с) cleanup гарантированно ре-enqueue'ит живой job → новый worker берёт SELECT pending без FOR UPDATE SKIP LOCKEDдва worker'а параллельно долбят забаненный NSPD-сервис (углубляя бан), счётчики done/failed затираются.

Бонус: status='paused' (после 8 WAF подряд) воскресает через минуту → WAF-защита аннулирована.

Fix (Вариант A+C из issue)

Module-level константы + параметризованный SQL (CAST(:x AS interval) per psycopg v3):

  • _ZOMBIE_RUNNING_THRESHOLD = "6 minutes" — выше max WAF backoff (4 мин).
  • _ZOMBIE_PAUSED_THRESHOLD = "30 minutes" — реальная WAF-пауза.

Beat-расписание (1 мин tick) НЕ тронут. WAF-логика (вариант B) и FOR UPDATE SKIP LOCKED для таргетов (вариант «идеальный») вне scope этого PR — отдельные follow-up'ы.

Test plan

  • 4 новых юнит-теста в test_nspd_geo.py: psycopg v3 guard, оба порога биндятся, два предиката вместо одного, paused > running.
  • pytest tests/workers/test_nspd_geo.py18 passed.
  • pytest -k nspd_geo18 passed, 1 skipped.
  • ruff check → All checks passed.
  • grep ':[a-z_]+::[a-z]' → clean (psycopg v3).

Closes #1215

## Summary Closes #1215 (P2 concurrency, prod). `cleanup_zombies` с порогом 2 мин ре-enqueue'ил живой WAF-job worker'а → дубль NSPD-запросов углублял бан. ## Bug `nspd_geo` worker в WAF-ветке: `wait = 30 * min(consecutive_waf, 8) = до 240с`, heartbeat коммитится **ДО** `time.sleep(wait)`. `cleanup_zombies` бежит каждую минуту с порогом `INTERVAL '2 minutes'` для `status IN ('running', 'paused')`. При `consecutive_waf >= 5` (sleep 150–240с > 120с) cleanup **гарантированно** ре-enqueue'ит живой job → новый worker берёт SELECT pending без `FOR UPDATE SKIP LOCKED` → **два worker'а параллельно долбят забаненный NSPD-сервис** (углубляя бан), счётчики `done/failed` затираются. Бонус: `status='paused'` (после 8 WAF подряд) воскресает через минуту → WAF-защита аннулирована. ## Fix (Вариант A+C из issue) Module-level константы + параметризованный SQL (`CAST(:x AS interval)` per psycopg v3): - `_ZOMBIE_RUNNING_THRESHOLD = "6 minutes"` — выше max WAF backoff (4 мин). - `_ZOMBIE_PAUSED_THRESHOLD = "30 minutes"` — реальная WAF-пауза. Beat-расписание (1 мин tick) НЕ тронут. WAF-логика (вариант B) и `FOR UPDATE SKIP LOCKED` для таргетов (вариант «идеальный») вне scope этого PR — отдельные follow-up'ы. ## Test plan - [x] 4 новых юнит-теста в `test_nspd_geo.py`: psycopg v3 guard, оба порога биндятся, два предиката вместо одного, paused > running. - [x] `pytest tests/workers/test_nspd_geo.py` → **18 passed**. - [x] `pytest -k nspd_geo` → **18 passed, 1 skipped**. - [x] `ruff check` → All checks passed. - [x] `grep ':[a-z_]+::[a-z]'` → clean (psycopg v3). Closes #1215
bot-backend added 1 commit 2026-06-13 05:29:31 +00:00
fix(nspd_geo): split zombie thresholds running=6m / paused=30m (#1215)
All checks were successful
CI / changes (push) Successful in 6s
CI / frontend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 5s
CI / frontend-tests (pull_request) Has been skipped
CI / backend-tests (push) Successful in 6m24s
CI / backend-tests (pull_request) Successful in 6m23s
787f8a25b9
В WAF-ветке `nspd_geo` worker спит до 240с (4 мин при consecutive_waf=8),
heartbeat коммитится ДО сна. Старый `cleanup_zombies` (`* * * * *`) с
порогом `INTERVAL '2 minutes'` для status IN ('running', 'paused')
гарантированно ре-enqueue'ил **живой** WAF-job:
- Два worker'а параллельно долбили забаненный NSPD-сервис, углубляя бан.
- SELECT pending без `FOR UPDATE SKIP LOCKED` → дубли запросов.
- Counter'ы done/failed двух инстансов затираются друг другом.

Бонус-баг: status='paused' (после 8 WAF подряд) воскресал через минуту,
аннулируя WAF-защиту.

Patch: разделил пороги через module-level константы и параметризованный
SQL (CAST(:x AS interval) per psycopg v3 rule):
- `_ZOMBIE_RUNNING_THRESHOLD = "6 minutes"` — выше max WAF backoff (4 мин).
- `_ZOMBIE_PAUSED_THRESHOLD = "30 minutes"` — реальная WAF-пауза.

Beat-расписание (1 мин tick) не тронуто. WAF-логика / heartbeat-in-loop
(вариант B) / FOR UPDATE SKIP LOCKED (вариант «идеальный») вне scope.

4 новых юнит-теста (test_nspd_geo.py): psycopg v3 guard, оба порога
биндятся, два предиката вместо одного, paused > running.
18/18 nspd_geo тестов зелёные. ruff clean.

Closes #1215
bot-backend merged commit b00e338667 into main 2026-06-13 05:37:09 +00:00
bot-backend deleted branch fix/nspd-zombie-threshold-vs-waf-backoff 2026-06-13 05:37:09 +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#1254
No description provided.