2 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 49b85ab1d6 |
perf(site-finder): air-quality TTL-кэш + neighbors_summary 2→1 SQL (#1130 Phase B)
All checks were successful
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 6m28s
CI / backend-tests (pull_request) Successful in 6m24s
Deploy / changes (push) Successful in 6s
Deploy / build-frontend (push) Has been skipped
Deploy / build-backend (push) Successful in 1m37s
CI / changes (push) Successful in 6s
Deploy / build-worker (push) Successful in 3m3s
Deploy / deploy (push) Successful in 1m24s
Phase B продолжает hot-path latency cut'ы analyze_parcel поверх Phase A (PR #1194): 1. get_air_quality_cached в weather_cache (третий TTL-слот рядом с forecast/climate): - hit-TTL 1h (hourly bucket Open-Meteo), negative-TTL 5min. - timeout 5s → 2s. - single-flight под per-cache threading.Lock. - Снимает 0.26с на cache-hit и до 5с при DNS-fail (тот же антипаттерн, что был у forecast/climate до Phase A). 2. _neighbors_summary: 2 SQL → 1 statement. - WITH neighbors AS (... LIMIT 30), overlaps AS (... LIMIT 5) - + COALESCE(json_agg(row_to_json(...)), '[]'::json) в основном SELECT. - Один round-trip вместо двух (~-47 ms на каждый analyze). - Семантика идентична: те же ST_DWithin(geom, point, 100m) для neighbors, ST_Intersects + ST_Area для overlaps. Anti-fakes guards (_COST_PER_M2_MIN/MAX, float() try/except, >50 m² overlap) сохранены. Формат возвращаемых dict не меняется (frontend контракт). 5 новых юнит-тестов в test_weather_cache.py (TestAirQualityCache: hot-hit, negative, empty-hourly, separate-from-weather, ttl-expires). 7 файлов API-тестов обновили patch-цель _fetch_air_quality_sync → get_air_quality_cached. Refs #1130 |
|||
| 97ff0c9e81 |
perf(site-finder): TTL-кэш Open-Meteo в analyze hot-path (#1130 Phase A)
All checks were successful
CI / frontend-tests (pull_request) Has been skipped
CI / changes (push) Successful in 7s
CI / frontend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 6s
CI / backend-tests (push) Successful in 6m28s
CI / backend-tests (pull_request) Successful in 6m26s
Deploy / build-frontend (push) Has been skipped
Deploy / changes (push) Successful in 6s
Deploy / build-backend (push) Successful in 1m41s
Deploy / build-worker (push) Successful in 4m51s
Deploy / deploy (push) Successful in 1m24s
Два внешних HTTP-вызова в analyze (_fetch_weather_sync + _fetch_seasonal_weather_sync) сидели без кэша. На проде в private network с restricted egress DNS до *.open-meteo.com фейлит и каждый replay висит полный timeout (5s + 15s = до 20с лишних на analyze). Профиль cProfile одного replay: 2.38с в httpx + 1.73с в SSL recv. Вынес логику в app/services/weather_cache.py: - TTL hit: forecast 6h, climate normals 7d. Negative-cache: 5min (DNS-fail не повторяет timeout на каждый analyze, восстановление подхватывается через ~5 минут). - Ключ — округлённые до 0.01° (~1 км) координаты. - Single-flight: per-cache threading.Lock + check-then-fetch-then-store. - Тайм-ауты сокращены: forecast 5s→2s, climate 15s→3s. Контракт для caller'а не изменился: None по-прежнему допустим (env_ok-флаг в _build_environmental_score). 7 файлов API-тестов обновили patch-цели на новые имена get_weather_cached / get_seasonal_weather_cached. 11 новых юнит-тестов в tests/services/test_weather_cache.py (single-flight через threading.Barrier(16), TTL expire через monkeypatch _now, изоляция forecast vs climate). Refs #1130 |