perf(tasks): лимиты/пагинация /api/tasks и дельта-синк в SQL
Шаги 0.10 и 0.9 плана production-готовности: - дефолт limit 50, кап 200; списковые ответы без description - курсорная пагинация (updated_at, id) + nextCursor - since-фильтр дельта-синка перенесён в SQL (updated_at >= since) - embedding pre-count через COUNT(*) вместо выгрузки миллиона строк - 11 новых юнит-тестов (56/56 зелёные) Инвариант поиска сохранён: search-ветка /api/tasks?search= не изменена
This commit is contained in:
@@ -115,3 +115,28 @@
|
||||
- Как проверялось: `docker run --entrypoint id` → uid=1001(app); `/app/data` и `/tmp` доступны на запись от app; document-worker на проде поднялся и healthy сразу.
|
||||
- Влияние на поиск/UX: нет.
|
||||
- Подводные камни: named volume `crm_crm_data` на проде был root-owned — на сервере заранее выполнен `chown -R 1001:1001` на `crm_crm_data` и `crm_crm_uploads` (иначе app не смог бы писать в /app/data). При развёртывании на новых серверах — учитывать.
|
||||
|
||||
---
|
||||
|
||||
## [0.10] Лимиты и пагинация в /api/tasks
|
||||
|
||||
- Статус: ✅ done
|
||||
- Зачем: листинг не отдаёт до 10000 полных строк (аудит, Фаза 0).
|
||||
- Что изменено:
|
||||
- `server/utils/task-pagination.ts` — `clampTasksLimit` (дефолт 50, кап 200), курсор `<updatedAt>_<id>` (parse/build).
|
||||
- `server/storage/tasks-core.storage.ts` — дефолт limit 50/кап 200 на уровне storage; опция `excludeDescription` (списковые ответы без тяжёлой text-колонки); курсорная пагинация `(updated_at, id)` с тай-брейкером; новый `getTasksCountByOrganization`.
|
||||
- `server/routes/task-crud-list.routes.ts` — кап на роуте, параметр `cursor`, в ответе `nextCursor`; ключ minimal-кэша включает limit/курсор.
|
||||
- Caller'ы: MCP search_tasks limit 500→200; embedding.service — pre-count через `getTasksCountByOrganization` вместо выгрузки миллиона строк; ShareTarget.tsx — явный `?limit=200`.
|
||||
- `tests/task-pagination.test.ts` — 11 юнит-тестов (кламп, курсор, round-trip).
|
||||
- Как проверялось: `.toSQL()` — курсор и тай-брейкер корректны, списковый SELECT без description; `npm run check` чисто; `npx vitest run` 56/56.
|
||||
- Влияние на поиск/UX: search-ветка не изменена (SQL, лимиты, формат) — инвариант сохранён. Дельта: список без параметров — 50 строк вместо 10000, без description; кастомные JS-виджеты (Bots.tsx, CustomPageView) получают задачи без description (при необходимости дотягивают деталь).
|
||||
- Подводные камни: `/api/forms/:id/tasks` и `/with-fields` НЕ тронуты (там description остаётся — реестр/канбан/Гантт используют их).
|
||||
|
||||
## [0.9] Дельта-синхронизация: фильтр на уровне SQL
|
||||
|
||||
- Статус: ✅ done
|
||||
- Зачем: sync не выгружает все задачи всех форм (аудит, Фаза 0).
|
||||
- Что изменено: `getTasksWithFieldsByFormOptimized` и `getUsersByOrganization` принимают `since?: Date` → `WHERE updated_at >= since OR updated_at IS NULL` (паритет со старым JS-фильтром); JS-фильтры в `sync.routes.ts` удалены; initial sync не тронут.
|
||||
- Как проверялось: `.toSQL()` — условие в SQL; индекс `tasks_form_updated_idx` (0079) покрывает; vitest 56/56.
|
||||
- Влияние на поиск/UX: нет.
|
||||
- Подводные камни: `OR IS NULL` обязателен — старые строки с NULL updated_at должны попадать в дельту (паритет с `!t.updatedAt ||` в JS).
|
||||
|
||||
Reference in New Issue
Block a user