бэктест: выборка вразброс — ORDER BY id DESC садится на несколько улиц #3289
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
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#3289
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/backtest-scattered-sampling"
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?
Владелец заметил на лендинге: «почти все сделки из Чкаловского района». Разбор дал причину глубже.
Что нашлось
ORDER BY id DESCберёт последние вставленные строки, а Росреестр грузится пачками по домам — соседние id это один дом и одна улица.Замер на проде 31.08.2026, Екатеринбург, выборка 200:
На этой выборке стоит витрина: 20 строк с четырёх улиц, 16 из них с двух (Данилы Зверева — 9, Лучистая — 7).
Что замер показал, а чего НЕ показал
Пять прогонов по 200 сделок:
Заголовочная точность устояла. Опубликованные 14,5 % лежат внутри разброса представительной выборки — кластеризация их не раздувала. Я шёл проверять обратную гипотезу и получил опровержение; говорю прямо, потому что иначе это выглядело бы как «нашли и починили».
А вот покрытие коридором опубликовано как 88 % — это верх диапазона. При пересборке выборки величина гуляет 83–88 при медиане около 86. То есть печатается лучший прогон из пяти как единственный.
Отсюда следствие для страницы, более общее, чем одно число: лендинг публикует точечные значения разового прогона, а они гуляют на несколько пунктов при пересборке выборки. Правка самих чисел — отдельным заходом, здесь только инструмент.
Решения
Умолчание не тронуто. Докстринг
_sample_sqlобещает дефолтному пути побайтово тот же SQL ради замороженного регресс-гейта; смена умолчания молча обнулила бы сравнимость всей истории замеров. Режим выбирается флагом--spread scattered.Порядок по
md5(id || seed), а неrandom(). Нужен воспроизводимый порядок: сrandom()два прогона отличались бы и из-за правки, и из-за состава выборки, и разделить вклады было бы нечем. Тот же seed → та же выборка.Проверки
6 тестов, обе стороны. Фальсификация:
is _SAMPLE_SQL); сравнение текстов её бы пропустило, а докстринг обещает именно побайтовую неизменность;md5наrandom()→ падает проверка воспроизводимости.Отдельный тест держит, что оба режима отбирают по одним и тем же условиям — иначе разница между прогонами объяснялась бы не порядком, а другим набором сделок, и сравнение ничего бы не значило.
ruff checkиruff format --checkчисто.ORDER BY id DESC берёт последние ВСТАВЛЕННЫЕ строки, а Росреестр грузится пачками по домам: соседние id это один дом и одна улица. Замер на проде 31.08.2026 по Екатеринбургу, выборка 200: последние 200 по id : 38 разных улиц, до 22 сделок с ОДНОЙ улицы случайные 200 : 129 разных улиц, максимум 6 с одной На этой выборке стоит витрина лэндинга: 20 строк с ЧЕТЫРЁХ улиц, 16 из них с двух. Владелец заметил это как «почти все сделки из одного района». ЧТО ЗАМЕР ПОКАЗАЛ, а что нет. Пять прогонов по 200 сделок: recent MAPE 15.18% покрытие 88.75% scattered mera MAPE 14.96% покрытие 83.13% scattered alpha MAPE 15.97% покрытие 88.34% scattered bravo MAPE 13.69% покрытие 83.85% scattered charlie MAPE 14.36% покрытие 86.23% Заголовочная точность УСТОЯЛА — опубликованные 14,5% лежат внутри разброса представительной выборки. Кластеризация её не раздувала. А покрытие коридором опубликовано как 88% — это верх диапазона: при пересборке выборки величина гуляет 83-88 при медиане около 86. Публикуется лучший прогон из пяти как единственный. Правка самих чисел лэндинга — отдельным заходом. УМОЛЧАНИЕ НЕ ТРОНУТО. Докстринг _sample_sql обещает дефолтному пути побайтово тот же SQL ради замороженного регресс-гейта; смена умолчания молча обнулила бы сравнимость всей истории замеров. Режим выбирается флагом --spread. Порядок по md5(id||seed), а не random(): нужен ВОСПРОИЗВОДИМЫЙ порядок, иначе два прогона отличаются и из-за правки, и из-за состава выборки, и разделить вклады нечем. Тест держит обе стороны и проверен фальсификацией: сделать вразброс умолчанием — падает identity-проверка (сравнение текстов её бы пропустило), заменить md5 на random() — падает проверка воспроизводимости.