Порог: свод 2 GO −30 %, лист FORM_2/AHR −10 % и меньший размер; пороги не условие мержа, корректность (сравнение exact/strip-nulls) — жёсткое. Решения контроллера: план в две фазы, ветка B стеком на A, archive после merge B, база сравнения, замеры контроллером, pytest на PG16. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7.6 KiB
Proposal: тяжёлые GET листов и сводов — готовый JSON без pydantic-модели на строку и без null-полей
Jira: AURORA-1848 (эпик AURORA-1834). Gitea: #233. Источник цифр: perf/REPORT.md, разделы М9 и М10, и perf/report-data/20261026-s1/analysis/api-split.md (масштаб 1, медиана 10 повторов, напрямую на :8000).
Why
Тяжёлые GET (лист формы, свод 1, свод 2) отдают тысячи строк, в каждой сотни полей, большинство из них null. Ответ проходит через Python 4–5 раз: драйвер разбирает jsonb в dict (json.loads внутри окна X-DB-Time-Ms), роутер строит SheetResponse на каждую строку (api/src/api/v1/forms.py:27-35, summary.py:29-37), FastAPI валидирует response_model, сериализует в JSON, затем GZipMiddleware сжимает (api/src/main.py:248).
| запрос (масштаб 1) | полное, мс | X-DB-Time-Ms |
остаток | доля остатка | размер |
|---|---|---|---|---|---|
| свод 2 GO, 5 563 строки | 1 057 | 718 | 339 | 32 % | 30,6 МБ |
| свод 2 RF, 9 497 строк | 1 808 | 1 229 | 578 | 32 % | 52,0 МБ |
| лист FORM_2/AHR, 2 092 строки | 813 | 726 | 87 | 11 % | 7,6 МБ |
| отчёт проекта LIMIT, 1 364 строки | 343 | 307 | 36 | 11 % | 1,5 МБ |
Сами данные уже собраны в Postgres как jsonb. Разбирать их в Python, чтобы тут же собрать обратно, — лишняя работа. Явные null раздувают ответ и время разбора в браузере: на масштабе 2 свод 2 — 123–195 МБ.
What Changes
Два PR, B стеком на A (согласовано 28.10).
PR A — готовый JSON из Postgres, ответ по смыслу тот же.
GET /api/v1/form/{form_id}/sheet/{sheet},GET /api/v1/svod/1/{sheet}/{year},GET /api/v1/svod/2/{sheet}/{year}отдают тело, собранное из текста строк, который вернул Postgres (json_build_object(...)::text). Нет pydantic-модели на строку и нет валидацииresponse_model. Обёртка{"success","message","result","count"}прежняя.- Отдельным коммитом то же для GET отчётов проектов в
api/src/api/v1/projects.py, которые объявляютBaseListResponse[SheetResponse]. - После разбора JSON значения те же, что и раньше. Байты могут отличаться: пробелы и запись чисел (
1.50вместо1.5). - Заголовок
X-DB-Time-Msсохраняется на всех этих ответах.response_modelостаётся в декораторах ради OpenAPI. perf/: скрипт сравнения ответов до и после, плюс размер ответа на проводе вapi_split.py.
PR B — без null-полей в data + правка фронта.
- В ответах GET листа, свода 1 и свода 2 у объектов в
dataнет полей со значениемnull, на любой глубине (jsonb_strip_nulls). BREAKING для потребителя, который отличает «ключ есть и равен null» от «ключа нет». Единственный такой потребитель — наш фронт. Выгрузка Excel и WebSocket идут своими путями и не меняются. - Фронт: входящая правка соавтора (WebSocket
cell_updated, эхо PATCH) применяется и к ключу, которого нет в загруженной строке. В AURORA-1838 (PR #223)updateCellsуже вставляет отсутствующий ключ. B проверяет, что этого достаточно, и закрепляет поведение тестом.
Критерии приёмки (согласовано 28.10). Свод 2 GO — полное время ниже минимум на 30 % после B. Лист FORM_2/AHR — минимум на 10 %, и распакованный размер меньше. Пороги не условие мержа: PR вливается по зелёным тестам и доказанной корректности (сравнение ответов на стенде), цифры — в PR, недобор — в follow-up.
Что НЕ меняется
- Ответы на правку — PATCH
/cell,/cells, POST/DELETE/line, WebSocketcell_updatedи правки FORM_3 — по-прежнему отдаютnullявно: очистка ячейки должна дойти до соавторов. - Выгрузка Excel и WebSocket читают данные через прежние методы репозиториев и сервисов. Эти методы остаются без изменений.
- SQL-функции и миграции. Новой миграции нет.
- Отчёты проектов в PR B: в них
nullостаются. - Сжатие (
GZipMiddleware, уровень 1), постраничная загрузка (М10).
Capabilities
New Capabilities
sheet-read-api: контракт ответа тяжёлых GET (лист, своды 1 и 2, отчёты проектов) — обёртка, строки, отсутствие null-полей, заголовокX-DB-Time-Ms, схема OpenAPI, неизменность путей записи и выгрузки, критерии производительности.realtime-sheet-updates: как фронт применяет входящие правки соавторов к строкам, загруженным без null-полей, и как показывает отсутствующие значения.
Modified Capabilities
Нет: openspec/specs/ пуст.
Impact
- Бэк:
api/src/api/v1/forms.py,summary.py,projects.py(только GET),api/src/repository/sheet_repository.py,summary_repository.py,project_repository.py,api/src/services/sheet_service.py,summary_service.py,project_service.py(новые методы рядом со старыми), новый хелпер ответа вapi/src/api/v1/, тесты вapi/tests/unitиapi/tests/integration. - Фронт (PR B):
web/src/components/RealtimeTable/utils/cellUtils.js(если #223 не покрывает всё), тестыcellUtils.test.js. perf/:analysis/api_split.py(новое поле), новыйanalysis/compare_responses.py. В PR B — потребители GET вperf/perfkit, если они обращаются к ключам, которые стали отсутствовать.- Связь с задачами: AURORA-1838 (PR #223) — B без неё не мержится. AURORA-1844/1850 — контракт строк ответа на правку с явными
null, этот change его не трогает. AURORA-1846 (PR #232) меняет содержимоеdataFORM_4, этот change — упаковку; общий участок — фикстураdb_sessionвapi/tests/integration/conftest.py. AURORA-1847 — SQL листа FORM_2/AHR: дальнейшее ускорение листа сверх порога этого change (−10 % и меньший размер) — там.