Производительность и нагрузка (черновик)
Актуально на: 2026-02-13
Цель: выдержать сценарий “до 10 000 одновременных пользователей”, которые редактируют бизнес‑планы (workspace) и периодически сохраняют изменения.
Обновлено: 2026-02-13 (см. CHANGELOG.md).
Самые дорогие операции (что убивает БД при 10k)
-
Автосейв workspace state
Ранее сохранение вызывало транзакцию сdeleteMany → createManyдля продуктов и календаря при каждом автосейве. При частом автосейве (≤ 1–2 сек) это быстро превращается в тысячи тяжёлых write-транзакций/сек. -
Проверка доступа на каждый save/load
Если на каждый save делатьproject.findFirst(...)по membership, это добавляет лишние read-запросы, которые “съедают” пропускную способность. -
Частые сохранения UI-настроек
saveUserUiSettingsделаетfindUnique + upsert. При маленьком дебаунсе создаёт лишнюю write-нагрузку.
Что уже сделано в коде (для защиты от нагрузки)
app/workspace-client-legacy.tsx: автосейв замедлён до 5 секунд и не инициирует revalidate.lib/project-workspace-store.ts: добавлен режим сохраненияnormalize: "light" | "full" | "none".lightсохраняет толькоProjectInfo,ProjectSettingsи JSON‑снапшот (ProjectWorkspaceState) без массовых delete/create.fullоставлен для редких операций (шаблоны/миграции/явное “финальное сохранение”).
lib/project-workspace-store.ts: при загрузке workspace приоритет отдаётся самому свежему источнику (если JSON‑снапшот новее нормализованных таблиц — возвращается снапшот).app/(workspace)/projects/[projectId]/actions.ts: добавлен короткий TTL‑кэш проверки доступа и троттлинг обновленияProject.name.app/workspace-client-legacy.tsx: сохранение UI‑настроек замедлено до 2.5 секунд.
Рекомендации для продакшена (обязательно при 10k)
База данных
- Использовать пулер (например, PgBouncer) и ограничить коннекты от приложений.
- Включить
pg_stat_statements, смотреть топ запросов по времени/кол-ву. - Следить за размером JSON (
ProjectWorkspaceState.state) и частотой UPDATE.
Приложение
- Разделить “черновой автосейв” (snapshot-only) и “публикацию/финальное сохранение” (full normalize) на уровне UI/UX.
- Вынести тяжёлые операции (нормализацию, расчёты, генерацию документов) в фоновые задачи (очередь).
- Добавить rate-limit на сохранения (на уровне API/ingress) по
userId+projectId.
План нагрузочного теста (что измеряем)
Метрики:
- RPS / p95 / p99 для: открытие workspace, автосейв, список проектов.
- Кол-во запросов к БД на одно действие пользователя (цель: минимизировать).
- Кол-во write-транзакций/сек и среднее время транзакции.
Сценарии:
- 10k пользователей открывают
/projects/[id](read-heavy). - 10k пользователей редактируют и делают автосейв раз в 5–10 сек (write-heavy).
- 1–5% пользователей создают проект/шаблон (burst writes).