stats_service.home() на каждый запрос главной прогоняет SCORED_CTE четыре раза: один раз в leaderboard() и ещё трижды внутри profile_stats() — _overall_for_user, _faction_breakdown, _recent_form. Все три выбирают из одного набора строк по одному и тому же user_id.
CTE джойнит match_participants с matches и группирует ничьи по всей таблице, поэтому стоимость растёт с числом партий во всём приложении, а не в группе игрока.
Почему не сделал в рамках #8: это перепроектирование запросов статистики, а не чистка — один проход с несколькими агрегатами (или выборка строк scored один раз с агрегацией в Python) требует аккуратной сверки цифр на реальных данных. Формула League Points при этом меняться не должна.
Сейчас, при десятках партий, разница незаметна — задача на будущее, и её удобно делать вместе с #23 (новая рейтинговая система), когда запросы всё равно будут переписываться.
`stats_service.home()` на каждый запрос главной прогоняет `SCORED_CTE` четыре раза: один раз в `leaderboard()` и ещё трижды внутри `profile_stats()` — `_overall_for_user`, `_faction_breakdown`, `_recent_form`. Все три выбирают из одного набора строк по одному и тому же `user_id`.
CTE джойнит `match_participants` с `matches` и группирует ничьи по всей таблице, поэтому стоимость растёт с числом партий во всём приложении, а не в группе игрока.
**Почему не сделал в рамках #8:** это перепроектирование запросов статистики, а не чистка — один проход с несколькими агрегатами (или выборка строк `scored` один раз с агрегацией в Python) требует аккуратной сверки цифр на реальных данных. Формула League Points при этом меняться не должна.
Сейчас, при десятках партий, разница незаметна — задача на будущее, и её удобно делать вместе с #23 (новая рейтинговая система), когда запросы всё равно будут переписываться.
Обнаружено при работе над #8.
Agent
added this to the v1.35 - промежуточная полировка версии 1.3 milestone 2026-09-09 18:52:38 +03:00
Уточнение по факту: проходов было не четыре, а пять — я не учёл в описании ещё один _overall_for_user для блока активной группы.
Итог: строки игрока тянутся одним запросом, а итог, разбивка по фракциям и форма считаются из них в Python; фильтр по группе — фильтрация того же набора. На главной осталось 2 прохода вместо 5, у профиля — 1 вместо 3.
Формула сглаженного рейтинга получила Python-версию рядом с SQL-версией на тех же константах (в SQL она нужна лидерборду). Чтобы реализации не разъехались — как это вышло с кэш-бастером аватара в #8 — добавлен тест, сверяющий цифры профиля с цифрами того же игрока в лидерборде.
Проверки:pytest — 93 passed. Существующие тесты статистики и профиля прошли без единой правки, то есть цифры не поехали. Второй новый тест считает запросы с SCORED_CTE на главной, чтобы оптимизация не отъехала назад при следующей правке витрин.
Статус:Status/In Review Осталось за вами: ревью и мёрж PR — задача закроется автоматически. По вехе v1.35 остаётся последняя задача — #33.
Работа выполнена, открыт PR: https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/pulls/43
**Уточнение по факту:** проходов было не четыре, а пять — я не учёл в описании ещё один `_overall_for_user` для блока активной группы.
**Итог:** строки игрока тянутся одним запросом, а итог, разбивка по фракциям и форма считаются из них в Python; фильтр по группе — фильтрация того же набора. На главной осталось 2 прохода вместо 5, у профиля — 1 вместо 3.
Формула сглаженного рейтинга получила Python-версию рядом с SQL-версией на тех же константах (в SQL она нужна лидерборду). Чтобы реализации не разъехались — как это вышло с кэш-бастером аватара в #8 — добавлен тест, сверяющий цифры профиля с цифрами того же игрока в лидерборде.
**Проверки:** `pytest` — 93 passed. Существующие тесты статистики и профиля прошли без единой правки, то есть цифры не поехали. Второй новый тест считает запросы с `SCORED_CTE` на главной, чтобы оптимизация не отъехала назад при следующей правке витрин.
**Статус:** `Status/In Review`
**Осталось за вами:** ревью и мёрж PR — задача закроется автоматически. По вехе v1.35 остаётся последняя задача — #33.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
stats_service.home()на каждый запрос главной прогоняетSCORED_CTEчетыре раза: один раз вleaderboard()и ещё трижды внутриprofile_stats()—_overall_for_user,_faction_breakdown,_recent_form. Все три выбирают из одного набора строк по одному и тому жеuser_id.CTE джойнит
match_participantsсmatchesи группирует ничьи по всей таблице, поэтому стоимость растёт с числом партий во всём приложении, а не в группе игрока.Почему не сделал в рамках #8: это перепроектирование запросов статистики, а не чистка — один проход с несколькими агрегатами (или выборка строк
scoredодин раз с агрегацией в Python) требует аккуратной сверки цифр на реальных данных. Формула League Points при этом меняться не должна.Сейчас, при десятках партий, разница незаметна — задача на будущее, и её удобно делать вместе с #23 (новая рейтинговая система), когда запросы всё равно будут переписываться.
Обнаружено при работе над #8.
Работа выполнена, открыт PR: #43
Уточнение по факту: проходов было не четыре, а пять — я не учёл в описании ещё один
_overall_for_userдля блока активной группы.Итог: строки игрока тянутся одним запросом, а итог, разбивка по фракциям и форма считаются из них в Python; фильтр по группе — фильтрация того же набора. На главной осталось 2 прохода вместо 5, у профиля — 1 вместо 3.
Формула сглаженного рейтинга получила Python-версию рядом с SQL-версией на тех же константах (в SQL она нужна лидерборду). Чтобы реализации не разъехались — как это вышло с кэш-бастером аватара в #8 — добавлен тест, сверяющий цифры профиля с цифрами того же игрока в лидерборде.
Проверки:
pytest— 93 passed. Существующие тесты статистики и профиля прошли без единой правки, то есть цифры не поехали. Второй новый тест считает запросы сSCORED_CTEна главной, чтобы оптимизация не отъехала назад при следующей правке витрин.Статус:
Status/In ReviewОсталось за вами: ревью и мёрж PR — задача закроется автоматически. По вехе v1.35 остаётся последняя задача — #33.