fix(site_finder): correct best-layouts supply fan-out (~8.5x) + objects-first perf #1944

Merged
bot-backend merged 1 commit from perf/best-layouts-objects-first into main 2026-06-27 05:58:38 +00:00
Collaborator

⚠️ USER-FACING: best-layouts supply/sold_pct/is_oversold меняются ~8.5x (к верному)

Это исправление видимых чисел в блоке Best Layouts (фронт BestLayoutsBlock.tsx). ТЗ-рекомендация и PDF НЕ меняются (они считаются из sum_deals, не из supply). Anton — учти, что цифры supply/sold_pct/срок-распродажи в этом блоке сдвинутся (старые скрины/деки на инфлированном supply устарели).

Баг (correctness)

domrf_kn_objects — snapshot-измерение (UNIQUE (obj_id, snapshot_date), ~8 снимков/obj_id). _SUPPLY_BATCH_SQL джойнил flats ко ВСЕМ object-snapshot строкам (без фильтра o.snapshot_date) → каждый flat считался ~8.5x. Итог (user-facing, best_layouts.py:571-611):

  • supply_units_in_radius завышен ~8.5x (показывается raw),
  • sold_pct_of_supply = deals_window/supply занижен ~8.5x,
  • is_oversold = ratio>100 недо-фаерит.
    Числитель (deals_window, objective velocity) НЕ инфлирован → отношение реально искажено (не сокращается). Чистое отношение без тюнингового порога → фикс однозначно корректен.

Фикс

Objects-first MATERIALIZED CTE: DISTINCT ON (obj_id) ORDER BY snapshot_date DESC (один ряд на obj_id = последний снимок) → ST_DWithin по этим координатам → join flats через существующий idx_kn_flats_obj. units теперь = один счёт на flat. Это ещё и выравнивает supply-знаменатель с deals-числителем (_COMPETITORS_IN_RADIUS_SQL уже использует DISTINCT ON latest snapshot).

Доказательства (prod, read-only)

  • One-per-flat: radius 1.5km, snapshot 2026-05-17 → units == count(*) == count(DISTINCT f.id) == 9612 для 65 объектов. Correction factor 8.56x @1.5km, 9.13x @1.0km.
  • Perf: 240ms/~28k buffers/6712 disk reads → 49ms/1554 buffers/0 disk reads (~5x). EXPLAIN: objects-first, Bitmap Index Scan idx_kn_flats_obj, no parallel seq scan.

Проверки

  • ruff · test_best_layouts 36 passed (вкл. новый fan-out guard test_supply_dedups_objects_to_latest_snapshot) · обновил stale EXPLAIN-зеркало в test_phantom_columns.
  • deep-code-reviewer: APPROVE — SQL-корректность (детерминизм DISTINCT ON через UNIQUE+NOT NULL), blast-radius (только фронт-таблица; recommendation/PDF/forecasts/competitors независимы), no hidden threshold calibration, anti-regression (только SQL-строка, shape не изменён).

database-expert + deep-code-reviewer независимо перепроверили downstream на проде (не по relayed authority).

## ⚠️ USER-FACING: best-layouts supply/sold_pct/is_oversold меняются ~8.5x (к верному) Это исправление **видимых чисел** в блоке Best Layouts (фронт `BestLayoutsBlock.tsx`). **ТЗ-рекомендация и PDF НЕ меняются** (они считаются из `sum_deals`, не из supply). Anton — учти, что цифры supply/sold_pct/срок-распродажи в этом блоке сдвинутся (старые скрины/деки на инфлированном supply устарели). ## Баг (correctness) `domrf_kn_objects` — snapshot-измерение (UNIQUE `(obj_id, snapshot_date)`, ~8 снимков/obj_id). `_SUPPLY_BATCH_SQL` джойнил flats ко ВСЕМ object-snapshot строкам (без фильтра `o.snapshot_date`) → каждый flat считался **~8.5x**. Итог (user-facing, `best_layouts.py:571-611`): - `supply_units_in_radius` завышен ~8.5x (показывается raw), - `sold_pct_of_supply = deals_window/supply` занижен ~8.5x, - `is_oversold = ratio>100` недо-фаерит. Числитель (`deals_window`, objective velocity) НЕ инфлирован → отношение реально искажено (не сокращается). Чистое отношение без тюнингового порога → фикс однозначно корректен. ## Фикс Objects-first **MATERIALIZED** CTE: `DISTINCT ON (obj_id) ORDER BY snapshot_date DESC` (один ряд на obj_id = последний снимок) → `ST_DWithin` по этим координатам → join flats через существующий `idx_kn_flats_obj`. `units` теперь = один счёт на flat. Это ещё и **выравнивает** supply-знаменатель с deals-числителем (`_COMPETITORS_IN_RADIUS_SQL` уже использует DISTINCT ON latest snapshot). ## Доказательства (prod, read-only) - **One-per-flat:** radius 1.5km, snapshot 2026-05-17 → `units == count(*) == count(DISTINCT f.id) == 9612` для 65 объектов. Correction factor **8.56x @1.5km, 9.13x @1.0km**. - **Perf:** 240ms/~28k buffers/6712 disk reads → **49ms/1554 buffers/0 disk reads** (~5x). EXPLAIN: objects-first, `Bitmap Index Scan idx_kn_flats_obj`, no parallel seq scan. ## Проверки - ruff ✅ · `test_best_layouts` **36 passed** (вкл. новый fan-out guard `test_supply_dedups_objects_to_latest_snapshot`) · обновил stale EXPLAIN-зеркало в `test_phantom_columns`. - **deep-code-reviewer: ✅ APPROVE** — SQL-корректность (детерминизм DISTINCT ON через UNIQUE+NOT NULL), blast-radius (только фронт-таблица; recommendation/PDF/forecasts/competitors независимы), no hidden threshold calibration, anti-regression (только SQL-строка, shape не изменён). database-expert + deep-code-reviewer независимо перепроверили downstream на проде (не по relayed authority).
bot-backend added 1 commit 2026-06-27 05:45:54 +00:00
fix(site_finder): correct best-layouts supply fan-out + objects-first perf
All checks were successful
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m55s
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Successful in 12m10s
7533bc333b
domrf_kn_objects is a snapshot dimension (UNIQUE (obj_id, snapshot_date), ~8
snapshots/obj_id). _SUPPLY_BATCH_SQL joined flats to ALL object-snapshot rows
(no o.snapshot_date filter), counting each flat ~8.5x → supply_units_in_radius
inflated ~8.5x, sold_pct_of_supply deflated ~8.5x, is_oversold under-fired
(all user-facing, best_layouts.py:571-611; sold_pct=deals/supply is a raw
ratio so no canceling).

Fix: dedup objects to one row per obj_id (latest-snapshot coords) via
DISTINCT ON in an objects-first MATERIALIZED CTE, then join domrf_kn_flats via
idx_kn_flats_obj. units now = one count per flat (prod cross-check at radius
1.5km: units == count(*) == count(DISTINCT f.id) == 9612 for 65 objects;
correction factor 8.56x at 1.5km, 9.13x at 1.0km). This also aligns the supply
denominator with the deals numerator (_COMPETITORS_IN_RADIUS_SQL already uses
DISTINCT ON latest snapshot).

Perf bonus: objects-first avoids the parallel seq scan of the ~376k-row flats
snapshot. radius 1.5km / snapshot 2026-05-17: 240ms/~28k buffers/6712 disk
reads -> 49ms/1554 buffers/0 disk reads (~5x).

Tests: add SQL-text fan-out guard (DISTINCT ON + MATERIALIZED, no bare
flats->objects join); update stale EXPLAIN mirror in test_phantom_columns.

USER-FACING: best-layouts supply/sold_pct/is_oversold/sell-out-months shift
~8.5x toward correct (frontend BestLayoutsBlock only; ТЗ recommendation + PDF
unchanged — they derive from sum_deals, not supply). Deep-reviewed (APPROVE).
bot-backend merged commit 7f2aa20be3 into main 2026-06-27 05:58:38 +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#1944
No description provided.