fix(scrape_kn): Redis-lock owner-token + resume retry on lock_held (#1216) #1255

Merged
bot-backend merged 1 commit from fix/scrape-kn-redis-lock-owner-token into main 2026-06-13 05:44:25 +00:00
Collaborator

Summary

Closes #1216 (P2 concurrency, prod). Два бага в Redis-lock'е scrape_kn:

  1. value="1" без owner-токена → release удаляет чужой lock когда sweep дольше TTL.
  2. SIGKILL/redeploy mid-sweep → stale lock, resume_kn_run skip без retry, run теряется до недельного beat.

Bug

  1. No owner token + tight TTL: _LOCK_TTL_SECONDS=30мин = заявленная длительность sweep'а. Если sweep >TTL → lock истекает, второй sweep стартует → первый при завершении r.delete сносит ЧУЖОЙ lock → третий параллельный.

  2. Stale lock + terminal skip: SIGKILL/redeploy mid-sweep — finally не выполняется, lock живёт до 30 мин в Redis с volume. worker_ready метит run 'zombie' и enqueue'ит resume_kn_run БЕЗ countdown. Ловит lock_held → возвращает {skipped: True} (без raise) → max_retries инертен. Никто больше не подхватывает: scan ищет только 'running', nspd_cleanup_zombies — не про kn. Run мёртв до недельного beat.

Fix

  • Owner token: lock value = uuid.uuid4().hex (32 chars hex). Release через Lua EVAL со скриптом check-and-delete: if GET == ARGV[1] then DEL else 0 end. Inline literal (однократный вызов в finally, register_script лишний).
  • TTL запас: 30 → 45 мин (полтора max sweep duration).
  • Resume retry: при lock_heldraise self.retry(countdown=300, max_retries=12)5 мин × 12 = ~1 час окно для подхвата вместо silent skip.
  • scrape_kn_region (scheduled) намеренно остаётся skipped — beat поднимет в следующий weekly tick (другая семантика, не зомби).

Test plan

  • 11 новых юнит-тестов: token uniqueness, Lua guard на чужой token, eval не дёргается при failed acquire, TTL=2700, contract Lua-скрипта, retry-not-skip, run_not_found всё ещё skipped, scheduled by-design skipped.
  • pytest tests/workers/tasks/test_scrape_kn.py11 passed.
  • pytest -k scrape_kn16 passed, 1 skipped.
  • ruff check → clean.

Closes #1216

## Summary Closes #1216 (P2 concurrency, prod). Два бага в Redis-lock'е `scrape_kn`: 1. `value="1"` без owner-токена → release удаляет **чужой** lock когда sweep дольше TTL. 2. SIGKILL/redeploy mid-sweep → stale lock, `resume_kn_run` skip без retry, run теряется до недельного beat. ## Bug 1. **No owner token + tight TTL**: `_LOCK_TTL_SECONDS=30мин = заявленная длительность sweep'а`. Если sweep >TTL → lock истекает, второй sweep стартует → первый при завершении `r.delete` сносит ЧУЖОЙ lock → третий параллельный. 2. **Stale lock + terminal skip**: SIGKILL/redeploy mid-sweep — `finally` не выполняется, lock живёт до 30 мин в Redis с volume. `worker_ready` метит run `'zombie'` и enqueue'ит `resume_kn_run` БЕЗ countdown. Ловит lock_held → возвращает `{skipped: True}` (без `raise`) → max_retries инертен. Никто больше не подхватывает: scan ищет только `'running'`, nspd_cleanup_zombies — не про kn. Run **мёртв до недельного beat**. ## Fix - **Owner token**: lock value = `uuid.uuid4().hex` (32 chars hex). Release через Lua `EVAL` со скриптом check-and-delete: `if GET == ARGV[1] then DEL else 0 end`. Inline literal (однократный вызов в finally, `register_script` лишний). - **TTL запас**: 30 → **45 мин** (полтора max sweep duration). - **Resume retry**: при `lock_held` → `raise self.retry(countdown=300, max_retries=12)` — **5 мин × 12 = ~1 час** окно для подхвата вместо silent skip. - `scrape_kn_region` (scheduled) **намеренно** остаётся `skipped` — beat поднимет в следующий weekly tick (другая семантика, не зомби). ## Test plan - [x] 11 новых юнит-тестов: token uniqueness, Lua guard на чужой token, `eval` не дёргается при failed acquire, TTL=2700, contract Lua-скрипта, retry-not-skip, run_not_found всё ещё skipped, scheduled by-design skipped. - [x] `pytest tests/workers/tasks/test_scrape_kn.py` → **11 passed**. - [x] `pytest -k scrape_kn` → **16 passed, 1 skipped**. - [x] `ruff check` → clean. Closes #1216
bot-backend added 1 commit 2026-06-13 05:36:40 +00:00
fix(scrape_kn): Redis-lock owner-token + resume retry on lock_held (#1216)
All checks were successful
CI / changes (push) Successful in 6s
CI / frontend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 6s
CI / frontend-tests (pull_request) Has been skipped
CI / backend-tests (push) Successful in 6m26s
CI / backend-tests (pull_request) Successful in 6m27s
492574fe17
Два concurrency-бага в scrape_kn:

1. Lock value="1" без owner-токена + TTL=30мин = заявленной длительности
   sweep'а (нулевой запас). Sweep > TTL → lock истекает, второй sweep
   стартует, первый при завершении безусловным r.delete сносит ЧУЖОЙ
   lock → возможен третий параллельный.

2. SIGKILL/redeploy mid-sweep: finally не выполняется, lock живёт до
   30 мин. worker_ready метит run 'zombie' и enqueue'ит resume_kn_run
   БЕЗ countdown, который ловит lock_held и возвращает skipped без
   retry → run теряется до недельного beat.

Patch:
- _region_lock: value = uuid4().hex; release через Lua check-and-delete
  (`if GET == ARGV[1] then DEL else 0 end`) — не сносим чужой lock.
- _LOCK_TTL_SECONDS 30 → 45 мин (полтора max sweep duration, запас).
- resume_kn_run: при lock_held → raise self.retry(countdown=300,
  max_retries=12) → ~час окно для подхвата вместо silent skip.
- scrape_kn_region (scheduled) намеренно остаётся skipped — beat
  поднимет в следующий weekly tick (другая семантика).

11 новых юнит-тестов (token uniqueness, Lua guard, retry-not-skip,
existing behaviors). 16/16 scrape_kn тестов зелёные. ruff clean.

Closes #1216
bot-backend merged commit c75e6a4cfa into main 2026-06-13 05:44:25 +00:00
bot-backend deleted branch fix/scrape-kn-redis-lock-owner-token 2026-06-13 05:44:25 +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#1255
No description provided.