From 62a5c64f77c057d9fb2b03c2ddd017a28226b79f Mon Sep 17 00:00:00 2001 From: Dusan Vojacek Date: Fri, 12 Jun 2026 15:48:10 +0200 Subject: [PATCH] =?UTF-8?q?HOTFIX=20web:=20/status/full=20500=20(str?= =?UTF-8?q?=E2=86=92tzinfo),=20/plan/compare=20500=20(chyb=C4=9Bj=C3=ADc?= =?UTF-8?q?=C3=AD=20comparison),=20canonical=20PV=20fn=204.5s=E2=86=920.4s?= =?UTF-8?q?=20(force=5Fcustom=5Fplan)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) full_status._iso_utc dostával z JSONB bundle stringy → AttributeError → 500 celého /status/full; nyní parsuje přes _parse_ts. 2) /plan/compare: NameError — 'comparison = _bundle_from_current(compare_raw)' se nikdy nesestavilo (smazaný řádek), endpoint vždy 500. 3) fn_forecast_pv_slots_range_canonical_ab: PG 18 cachuje plány SQL funkcí → generický plán 4.5 s / 607k buffers; set plan_cache_mode=force_custom_plan → 0.4 s / 34k (změřeno explain analyze na živé DB). Táhne /plan/current, /plan/compare i rolling plánovač. Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/app/routers/full_status.py | 5 ++++- backend/app/routers/plan.py | 1 + .../R__088_fn_forecast_pv_slots_range_canonical_ab.sql | 3 +++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/backend/app/routers/full_status.py b/backend/app/routers/full_status.py index 0bd917b..9c24a50 100644 --- a/backend/app/routers/full_status.py +++ b/backend/app/routers/full_status.py @@ -40,7 +40,10 @@ HEARTBEAT_STALE_SEC = 300 EXPECTED_TOMORROW_PRICE_SLOTS = 90 -def _iso_utc(dt: datetime | None) -> str | None: +def _iso_utc(dt: datetime | str | None) -> str | None: + # JSONB bundle z fn_site_full_status nese timestampy jako stringy — parsovat, + # jinak .tzinfo na str = AttributeError → 500 celého /status/full. + dt = _parse_ts(dt) if dt is None: return None if dt.tzinfo is None: diff --git a/backend/app/routers/plan.py b/backend/app/routers/plan.py index 21ada76..0bc5036 100644 --- a/backend/app/routers/plan.py +++ b/backend/app/routers/plan.py @@ -207,6 +207,7 @@ async def get_plan_compare( raise HTTPException(status_code=404, detail="No comparison plan") active = _bundle_from_current(active_raw) + comparison = _bundle_from_current(compare_raw) diff, slot_diffs = _build_plan_diff(active, comparison) return PlanningCompareResponseModel( active=active, diff --git a/db/routines/R__088_fn_forecast_pv_slots_range_canonical_ab.sql b/db/routines/R__088_fn_forecast_pv_slots_range_canonical_ab.sql index ae5b917..c2379b8 100644 --- a/db/routines/R__088_fn_forecast_pv_slots_range_canonical_ab.sql +++ b/db/routines/R__088_fn_forecast_pv_slots_range_canonical_ab.sql @@ -28,6 +28,9 @@ returns jsonb language sql stable set work_mem = '64MB' +-- PG 18 cachuje plány SQL funkcí → generický plán bez znalosti hodnot parametrů +-- (4.5 s / 607k buffers); s custom planem dle skutečných hodnot 0.4 s / 34k. +set plan_cache_mode = force_custom_plan as $fn$ with tz as ( select coalesce(nullif(trim(s.timezone), ''), 'Europe/Prague') as tz_name