perf(sf): concurrent geoportal-вызовы в ИРД-блоке analyze (#1102) #1123
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#1123
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/ird-analyze-concurrent-geoportal"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Что
Чинит latency-регрессию
/parcels/{cad}/analyzeпосле включенияenable_ird_analyze: 2 geoportal-вызова (krt WFS + zone_regulation) шли последовательно → ~9.4s на холодном cad (verified prod). Теперь — конкурентно.Как
build_ird_analyze_block:_krt_atуходит вThreadPoolExecutor(чистая сеть, безdb),_zone_regulation_at(db,…)выполняется в основном потоке параллельно → итог ≈max(call)≈ ~4s вместо суммы ~8s.Sessionстрого в основном потоке (krt-потокdbне трогает) — инвариант соблюдён, без отдельного SessionLocal.Тест
test_ird_analyze.py— 7 passed: оба источника попадают в block; исключение в krt →krt=[], zone_regulation всё равно заполнен; Session не шарится.Закрывает latency-хвост #1102. Часть #1067.
✅ APPROVE — staff+ review (concurrency change in analyze hot path).
Two real risks verified clean (read source, not just diff):
EKBGeoportalClientis stateless-per-call: onlyself.timeout(set once in__init__, read-only after); no memoizedself._keys/cache, no lazy-init, no cookie/token mutation. HTTP goes through module-level_http_get_json/_http_post_json(fresh calls),_search_keys/_urban_cardbuild local dicts. The two concurrent calls share onlyself.timeout+ httpx transport (thread-safe pool). Safe.get_or_fetch_zone_regulation(db, lon, lat, *, city='ekb', client=None); test mocks use(db, lon, lat, client=None)→ positional order exact, not vacuous.Session-safety invariant correct:
_krt_at(client, lon, lat)is db-free (only consumer ofdbis_zone_regulation_at, which stays on main thread).test_session_not_shared_between_threadsvalidates at runtime viathreading.current_thread().with ThreadPoolExecutorjoins (shutdown wait=True) before exit → no leak/deadlock; graceful chain complete (_krt_atcatches + future.result()catch +_zone_regulation_atcatches).Additive to #1115 (gated behind enable_ird_analyze + try/except in parcels.py) → worst case degrades to existing graceful-fail. 🟡 trivial:
max_workers=1reads counter-intuitive (one task) — cosmetic, not actionable.Merging (squash).