perf(factory): параллелить независимые чтения сборки документа (resolveParallel) #103

Merged
ant merged 4 commits from perf/factory-parallel-fetch into dev 2026-06-05 19:05:26 +00:00
Owner

Проблема

После ускорения PDF (тёплый пул, #100) основное время генерации (~1.5с) ушло в сборку данных документа. В каждом Action.generateDocument чтения шли строго последовательно: getTemplate (2 сетевых запроса в explorer) → getMeta(getCurrentBlock) → mongo (getUser/getCooperative/getProject/getVars) → udata/request/meet. Сетевые запросы складывались в задержку.

Решение

Хелпер DocFactory.resolveParallel({...}) — резолвит взаимно независимые источники через Promise.all. В каждом из 46 Action-факторов независимый префикс (template + mongo-чтения + независимые meet/request/udata) сведён в один resolveParallel; зависимые вызовы оставлены последовательными после батча, порядок и аргументы сохранены:

  • getMeta({ title: template.title }) — зависит от template (нельзя в батч: thunk'и в Promise.all исполняются до инициализации деструктурированной template → TDZ);
  • getFullName / getCommonUser — от user;
  • getDecision / getApprovedDecision — от coop (+ meta.created_at);
  • getMeetQuestions / getGeneralMeetingDecision — от meet;
  • getProgram — от request.

Безопасность

Поведение не меняется — только параллелизуются заведомо независимые чтения. Условные ветки (bank_account по типу юзера, branch по is_branched, signature-логика) и гварды сохранены на местах. Файлы, где параллелить нечего (только template+meta, а meta зависит от template) — не тронуты.

Проверки

  • tsc --noEmit — чисто
  • eslint (src/Actions, src/Factory) — чисто
  • unbuild — exit 0

Базируется на dev (warm-pool #100 уже влит). Эффект: сетевой getTemplate идёт параллельно с mongo/udata/meet вместо последовательной цепочки → остаток ~1.5с схлопывается к самому медленному одиночному запросу.

🤖 Generated with Claude Code

## Проблема После ускорения PDF (тёплый пул, #100) основное время генерации (~1.5с) ушло в **сборку данных документа**. В каждом `Action.generateDocument` чтения шли строго последовательно: `getTemplate` (2 сетевых запроса в explorer) → `getMeta`(getCurrentBlock) → mongo (`getUser`/`getCooperative`/`getProject`/`getVars`) → udata/request/meet. Сетевые запросы складывались в задержку. ## Решение Хелпер `DocFactory.resolveParallel({...})` — резолвит **взаимно независимые** источники через `Promise.all`. В каждом из **46 Action-факторов** независимый префикс (template + mongo-чтения + независимые meet/request/udata) сведён в один `resolveParallel`; зависимые вызовы оставлены последовательными после батча, порядок и аргументы сохранены: - `getMeta({ title: template.title })` — зависит от template (нельзя в батч: thunk'и в Promise.all исполняются до инициализации деструктурированной `template` → TDZ); - `getFullName` / `getCommonUser` — от user; - `getDecision` / `getApprovedDecision` — от coop (+ meta.created_at); - `getMeetQuestions` / `getGeneralMeetingDecision` — от meet; - `getProgram` — от request. ## Безопасность Поведение не меняется — только параллелизуются заведомо независимые чтения. Условные ветки (bank_account по типу юзера, branch по `is_branched`, signature-логика) и гварды сохранены на местах. Файлы, где параллелить нечего (только template+meta, а meta зависит от template) — **не тронуты**. ## Проверки - `tsc --noEmit` — чисто - `eslint` (src/Actions, src/Factory) — чисто - `unbuild` — exit 0 Базируется на dev (warm-pool #100 уже влит). Эффект: сетевой `getTemplate` идёт параллельно с mongo/udata/meet вместо последовательной цепочки → остаток ~1.5с схлопывается к самому медленному одиночному запросу. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude added 1 commit 2026-06-05 17:18:16 +00:00
perf(factory): параллелить независимые чтения сборки документа через resolveParallel
Typecheck / desktop (pull_request) Successful in 12m54s
Typecheck / controller (pull_request) Successful in 12m31s
0030f5a362
Сборка данных документа в каждом Action делала серию строго последовательных
await: getTemplate (2 сетевых запроса в explorer) + getMeta(getCurrentBlock) +
mongo-чтения (getUser/getCooperative/getProject/getVars) + udata/request/meet.
Сетевые запросы складывались в задержку ~1.5с поверх рендера PDF.

Добавлен хелпер DocFactory.resolveParallel({...}) — резолвит ВЗАИМНО
НЕЗАВИСИМЫЕ источники через Promise.all. В каждом из 46 Action-факторов
независимый префикс (template + mongo-чтения + независимые meet/request/udata)
сведён в один resolveParallel; зависимые вызовы оставлены последовательными
после батча, с сохранением порядка и аргументов:
  - getMeta({title: template.title}) — зависит от template (TDZ в Promise.all);
  - getFullName/getCommonUser — от user;
  - getDecision/getApprovedDecision — от coop (+meta.created_at);
  - getMeetQuestions/getGeneralMeetingDecision — от meet;
  - getProgram — от request.

Поведение не меняется — только параллелизуются независимые чтения. Условные
ветки (bank_account по типу юзера, branch по is_branched, signature) и гварды
сохранены на местах. Файлы, где параллелить нечего (только template+meta,
meta зависит от template) — не тронуты.

tsc + eslint + unbuild — зелёные.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-05 18:43:10 +00:00
perf(factory): системный Arial вместо base64 в html — рендер weasyprint ×10
Typecheck / controller (pull_request) Has been cancelled
Typecheck / desktop (pull_request) Has been cancelled
ddf050a02f
Главное горло генерации PDF: в каждый html инжектился @font-face с Arial через
data:base64 (~270 КБ TTF). weasyprint декодировал и парсил весь шрифт на КАЖДЫЙ
рендер — ~1.3 сек на документ в 754 байта (замер: тот же текст со встроенным
шрифтом 1282–1715 мс, без него 120–289 мс).

Ставим Arial СИСТЕМНО в образ controller (fontconfig) — парсится один раз и
кешируется. Шрифт тот же файл (декод прежнего ArialBase64 → arial.ttf), имя
семейства "Arial", глифы/вёрстка идентичны.

- controller/assets/fonts/arial.ttf — наш Arial (275 КБ)
- controller/Dockerfile — fontconfig + COPY шрифта + fc-cache
- factory Generator — убран @font-face data:base64, оставлен font-family: Arial
- удалён src/Fonts/arial.ts (367 КБ base64, больше не нужен; bundle 1.48 → 0.75 МБ)

Итог по всей серии (warm-pool + parallel-fetch + системный шрифт):
generateProjectOfFreeDecision ~5 сек → ~400 мс (×10).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-05 18:45:27 +00:00
fix(factory): arial.ttf как binary — controller/.gitattributes нормализовал EOL
Typecheck / desktop (pull_request) Successful in 13m29s
Typecheck / controller (pull_request) Has been cancelled
2496755c4e
В предыдущем коммите arial.ttf закоммитился битым: components/controller/.gitattributes
содержит `* text eol=lf`, что применилось и к бинарному шрифту → срезало 105 байт
\r (275572 → 275467), шрифт невалиден. Добавил `*.ttf/otf/woff binary` в
controller/.gitattributes (перекрывает правило выше) и перезаписал blob корректно.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude added 1 commit 2026-06-05 19:01:50 +00:00
docs(factory): зафиксировать паттерн параллелизации resolveParallel в CLAUDE.md
Typecheck / desktop (pull_request) Successful in 13m29s
Typecheck / controller (pull_request) Successful in 13m14s
26d4774307
Конвенция для Actions/*: независимые от data чтения — в resolveParallel,
зависимые (getMeta/getDecision/getMeetQuestions) — после батча; ловушка TDZ;
системный шрифт вместо base64.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ant approved these changes 2026-06-05 19:05:21 +00:00
ant merged commit 0966a97dde into dev 2026-06-05 19:05:26 +00:00
Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: C9S/mono#103