Andrey Epifancev 6e607b1647 docs(openspec): aurora-1843-svod2-server-paging — proposal, spec, design
Свод 2: серверная постраничная загрузка (поиск, три фильтра, total,
варианты фильтров) и компактный ответ; фронт догружает строки окном
страниц; замеры памяти, времени до первых строк, байтов и шага скролла.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 20:27:17 +04:00

12 KiB
Raw Blame History

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 — поиск подстроки без учёта регистра по тем же полям, что ищет экран сейчас;
    • smeta_type и smeta_direction — фильтры по виду сметы и направлению;
    • 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. Отдаёт общее число строк и варианты трёх колоночных фильтров («Вид сметы», «Направление», «ССП») для выбранного года и организаций.

  • SQL. Новая миграция:

    • DROP прежних v3.v_svod2_go и v3.v_svod2_rf (6 аргументов) и CREATE с новой сигнатурой, без перегрузок;
    • общий отбор строк свода 2 выносится в одну функцию, её используют и страницы, и варианты фильтров;
    • функция вариантов фильтров и проекция компактной строки.

    Всё совместимо с PostgreSQL 15. Отбор строк (какие строки попадают в свод) не меняется.

  • Экран свода 2.

    • Таблица и колонки остаются прежними.
    • После «Загрузить» приходит первая страница строк, остальные догружаются при прокрутке.
    • В памяти держится окно из нескольких страниц вокруг видимой области, а не весь ответ. Полоса прокрутки соответствует полному числу строк.
    • Поиск (debounce 300 мс уже есть) и три колоночных фильтра выполняются на сервере, варианты фильтров приходят с сервера.
    • «Найдено N из M» строится из total и считает и поиск, и колоночные фильтры.
    • Свод 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 добавлены страницы, компактный вид и варианты фильтров.
    • Скрипт сверки серверной выдачи со старым полным ответом на данных стенда.
  • Три PR в стеке:

    1. сервер — SQL, API, тесты;
    2. фронт — догрузка, поиск, фильтры;
    3. замер и сценарии 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.
  • Набор, порядок, ширины и форматирование колонок, единицы (руб./тыс./млн), закреплённые колонки, «Перейти к задачам». Подписи «Все ССП/РФ», «Загрузить» и «Обновить» и порядок действий пользователя (выбрать организации → «Загрузить») тоже прежние.

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/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/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.