Поиск, колоночные фильтры, варианты фильтров и «Найдено N из M» — с точной семантикой экрана (matchesSearch, MRT equals, faceting TanStack); регистр без зависимости от локали БД; пороги — цели, а не условие слияния; решения контроллера (три фазы/ветки, prefetch страницы 1, окружение тестов). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
13 KiB
Proposal: AURORA-1843 — свод 2: серверная постраничная загрузка и компактный ответ
Gitea issue #236, Jira AURORA-1843 (эпик AURORA-1834). Стек: AURORA-1835 (PR #217) → AURORA-1842 (PR #227) → эта задача.
Why
При выборе «Все ССП/РФ» свод 2 (/svod/2) получает весь лист одним ответом и держит его в памяти вкладки целиком. Поиск и колоночные фильтры при этом считаются в браузере по всем строкам.
Цифры из отчёта о производительности (perf/REPORT.md, М2, М9, М10; масштаб 1 и 2, CPU 1×):
| масштаб 1 | масштаб 2 | |
|---|---|---|
| ответ GO, распакованный | 30,5 МБ (5 563 строки, ~5,5 КБ на строку) | 123,2 МБ |
| ответ RF, распакованный | 52,7 МБ (9 497 строк) | 195,1 МБ |
| время ответа GO / RF | 1,06 / 1,8 с | 4,5–4,8 / 7,1–7,3 с |
| доля Python в ответе (pydantic на строку, JSON, gzip) | 32 % (0,34–0,58 с) | растёт с размером |
| шаг скролла GO / RF (до 1842) | 184 / 187 мс | 251 / 214–256 мс |
| ввод в поиск, главный поток | 0,2–0,3 с | GO 187–266 мс, RF 281–325 мс |
Пока открыт маршрут /svod/2, состояние страницы держит ответы обеих вкладок. GO «все» и RF «все» вместе дают около 83 МБ JSON на масштабе 1 и около 318 МБ на масштабе 2, плюс объекты строк таблицы. Рост шага на масштабе 2 при том же числе видимых строк отчёт связывает со сборкой мусора на такой куче: GC занимает 17 мс на шаг против 4–5 мс на листах. Это не подтверждено, но с объёмом данных согласуется.
Задача была аналитической: «предложить вариант с оценкой». 28.10 человек решил делать сразу реализацию с максимальным эффектом. Выбраны варианты C (серверная пагинация, поиск и фильтры на сервере) и B (компактный ответ) из черновика разведки. Вариант A (только политика кэша на фронте) не нужен.
What Changes
-
API
GET /api/v1/svod/2/{sheet}/{year}. У эндпоинта уже естьorg_ids,offset,limit,sort_byиsort_direction. Добавляются необязательные параметры:search— поиск с точно той же семантикой, что у экрана сейчас (matchesSearch: те же поля с тем же перекрытием, числа в записи JavaScript, регистр не зависит от локали БД);smeta_type,smeta_directionиorg_name— колоночные фильтры с семантикой фильтра таблицы экрана (equals: без учёта регистра и пробелов по краям);view=full|compact—compactотдаёт только поля, которые экран показывает.
Если в запросе есть хотя бы один новый параметр, в ответе появляется поле
total— число строк по всем условиям запроса без учётаoffset/limit.count, как и раньше, равен длинеresult.org_idsпо-прежнему задаёт выбор ССП/РФ и проверяется на права. -
Без новых параметров ответ байт-в-байт прежний. Это нужно Excel-выгрузке RF, интеграционным тестам,
perfkit.benchиperfkit.gen.verify. -
Новый эндпоинт
GET /api/v1/svod/2/{sheet}/{year}/facets. Отдаёт общее число строк, число строк по поиску («Найдено N из M») и варианты трёх колоночных фильтров («Вид сметы», «Направление», «ССП») с числами строк. Варианты считаются как сейчас на экране: с учётом поиска и фильтров двух других колонок. -
SQL. Новая миграция:
DROPпрежнихv3.v_svod2_goиv3.v_svod2_rf(6 аргументов) иCREATEс новой сигнатурой, без перегрузок;- общий отбор строк свода 2 выносится в одну функцию, её используют и страницы, и варианты фильтров;
- функция вариантов фильтров и проекция компактной строки.
Всё совместимо с PostgreSQL 15. Отбор строк (какие строки попадают в свод) не меняется.
-
Экран свода 2.
- Для пользователя всё остаётся таким же (решение 28.10): строки, поиск, варианты фильтров с числами, «Найдено N из M», сброс фильтров, вкладки, прокрутка. Отличаются только ожидание ответа сервера и заглушки при быстрой прокрутке.
- Таблица и колонки остаются прежними.
- После «Загрузить» приходят первые две страницы строк (вторая — заранее), остальные догружаются при прокрутке.
- В памяти держится окно из нескольких страниц вокруг видимой области, а не весь ответ. Полоса прокрутки соответствует полному числу строк.
- Поиск (debounce 300 мс уже есть) и три колоночных фильтра выполняются на сервере, варианты фильтров приходят с сервера.
- «Найдено N из M» считает, как и сейчас, только поиск.
- Свод 2 больше не кладёт ответы в кэш
SvodContext. Кэш остаётся только у свода 1, его поведение не меняется.
-
Стенд замеров
perf/.scroll.spec.ts: свод 2 учитывает строки-заглушки, добавлены метрики времени до первых строк и байтов, фильтр ждёт ответа сервера.- Новый сценарий
svod2-memory.spec.ts(GO «все» → RF «все» → GO, куча после GC). - В
api_split.py,explain_manual.pyиscale_compare.pyдобавлены страницы, компактный вид и варианты фильтров. - Скрипты сверки серверной выдачи со старым полным ответом на данных стенда, в том числе оракул на Node.js с дословной копией
matchesSearchи правил фильтров и вариантов таблицы. - Цифры замеров — цели, а не условие слияния: недобор уходит в follow-up (решение 28.10). Условие слияния — доказанная корректность.
-
Три PR в стеке:
- сервер — SQL, API, тесты;
- фронт — догрузка, поиск, фильтры;
- замер и сценарии
perf/.
Что НЕ меняется
- Excel-выгрузка свода 2 (
GET /api/v1/export/summary/2/...) — ни контракт, ни содержимое. Кнопки выгрузки свода 2 на фронте нет, поэтому под экранные фильтры её не подгоняем (решение 28.10). - Какие строки попадают в свод 2. Отбор по наличию данных — миграция 0049. Отбор RF по типу формы — баг O-5, он решается отдельно в AURORA-1917.
- Свод 1, сводные проектов,
SvodFilters(общий для сводов 1 и 2), кэшSvodContextдля свода 1. - Сортировка свода 2: на экране она выключена (
enableSorting: false), на сервере остаётся по умолчаниюorg_name asc. - Семантика поиска, колоночных фильтров, вариантов фильтров и «Найдено N из M», включая найденную странность поиска (поля
contract_summary, одноимённые полямcontract_detail, не ищутся) — описана в design.md как находка, не исправляется. - Набор, порядок, ширины и форматирование колонок, единицы (руб./тыс./млн), закреплённые колонки, «Перейти к задачам». Подписи «Все ССП/РФ», «Загрузить» и «Обновить» и порядок действий пользователя (выбрать организации → «Загрузить») тоже прежние.
Capabilities
New Capabilities
summary2-api: контракт API свода 2. Постраничная выдача сtotal, серверные поиск, фильтры и варианты фильтров с семантикой экрана, компактное представление строки, права доступа, совместимость прежнего ответа, замер размера ответа.summary2-screen: поведение экрана свода 2. Неизменное для пользователя поведение, догрузка строк страницами при прокрутке, ограниченное окно строк в памяти, поиск и фильтры через сервер, «Найдено N из M», ошибки. Замеры производительности: память, время до первых строк, шаг скролла.
Modified Capabilities
Нет: openspec/specs/ пока пуст.
Impact
- Бэк:
api/src/api/v1/summary.py(get_summary2и новый эндпоинт вариантов фильтров);api/src/services/summary_service.py;api/src/repository/summary_repository.py;api/src/domain/schemas.py(модели ответа сtotalи вариантов фильтров);- новая миграция
api/alembic/versions/00NN_svod2_server_paging.py+sql/00NN_svod2_server_paging.sql. Номер выбирается при rebase, см. design.md.
- Тесты бэка:
api/tests/integration/test_summary_api_smoke.pyи новые интеграционные тесты со своими данными свода 2;- векторы
api/tests/fixtures/js_number_text_vectors.json(запись чисел в JavaScript, сгенерированы Node.js); api/tests/unit/test_summary2_related_records_sql.py(перенацелить на новую миграцию);- unit-тесты сервиса и валидации параметров.
- Фронт:
web/src/api/summary.js;web/src/pages/Svod2Page/Svod2Page.jsx;- новые
web/src/pages/Svod2Page/svod2Paging.js(чистая логика окна и параметров, сnode --test) и хук загрузки страниц; svod2Styles.js(стиль строк-заглушек).
- Стенд:
perf/e2e/scenarios/scroll.spec.ts;- новый
perf/e2e/scenarios/svod2-memory.spec.ts; perf/analysis/api_split.py,perf/analysis/explain_manual.py,perf/analysis/scale_compare.py;- новые
perf/analysis/svod2_paging_check.pyиperf/analysis/svod2_search_oracle.mjs; perf/README.md.
- Связанные задачи:
- AURORA-1842 (PR #227, база стека) — виртуализация колонок и CSS-стили свода 2; от неё берётся результат шага скролла, который нельзя ухудшить.
- AURORA-1836 (PR #222, от
test) правит вSvod2Page.jsxвысоту строки (2,75 rem) и overscan строк. Окно страниц опирается на фиксированную высоту строки, при rebase брать её из 1836. - AURORA-1917 (баг O-5: в «РФ» попадают строки ССП) уменьшит ответ RF. Её нельзя вливать между замерами «до» и «после» 1843.
- AURORA-1848 (тяжёлые GET без pydantic на строку) правит тот же обработчик
get_summary2, нужна координация. - AURORA-1831/1844/1846 заняли номер миграции 0050.