perf(site-finder): TTL-кэш Open-Meteo в analyze hot-path (#1130 Phase A) #1194
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
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#1194
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "perf/analyze-weather-ttl-cache"
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?
Summary
Phase A для #1130 (
POST /analyzeтормозит): кэш для двух Open-Meteo HTTP-вызовов в hot-path, сокращены тайм-ауты, single-flight, negative-cache на DNS-fail.Раскопал на проде:
_fetch_weather_sync(timeout=5s) +_fetch_seasonal_weather_sync(timeout=15s) сидели без кэша. В backend-контейнере прода DNS до*.open-meteo.comфейлит (private network с restricted egress), и каждый replay висел полный timeout. Replay × 3 на проде после PR #1139:p50 = 14.88 s,min = 8.02 s(DNS resolved). cProfile показал: 2.38с в httpx-стеке + 1.73с в SSL recv.Что делает PR
Новый модуль
app/services/weather_cache.pyс двумя публичными функциями:get_weather_cached(lat, lon)→ 7-day forecast, hot-TTL 6h.get_seasonal_weather_cached(lat, lon)→ 30-летние climate normals, hot-TTL 7 суток (climate-data обновляется ~раз в год).(round(lat, 2), round(lon, 2))— ≈ 1 км, для одного cad всегда один ключ.threading.Lock+ check-then-fetch-then-store внутри lock'а. 16 параллельных потоков → ровно 1 GET (есть тест сthreading.Barrier(16)).time.monotonic()для expires_at (устойчиво к NTP-скачкам).Exception→logger.warning+ negative-cache +None. Контракт для caller'а не изменился (None по-прежнему допустим —_build_environmental_scoreиспользуетbool(weather)вenv_ok).parcels.py: удалены_fetch_weather_sync+_fetch_seasonal_weather_sync(вся логика перенесена побайтово — те же ключи в return dict), заменены два вызова вanalyze_parcelна новые.-132 / +22.7 файлов
tests/api/v1/test_*.pyобновилиpatch("...parcels._fetch_weather_sync"...)→patch("...parcels.get_weather_cached"...).Ожидаемый импакт
Прод
POST /analyze(cad с DNS-fail):~14s→~3-5scold-start, далее ms на cache-hit в 6h-окне. Если останется > 3s — Phase B (per-request memo для 68 БД-запросов, ~3.2с в hot-path).Test plan
pytest tests/services/test_weather_cache.py -v→ 11/11 passed (hit, negative, изоляция forecast/climate, single-flight через Barrier(16), TTL expire через monkeypatch_now).pytest -x tests/api/→ 195 passed, 2 skipped (no regression).ruff check+ruff format --check→ clean.POST /analyze× 3 на проде через qa-tester — confirmp50 ≤ 5s(vs до 14.88s).Refs #1130