perf(s14): индексы + N+1 fix + bundle -50% + WebP variants + pool + Hangfire timing
Sprint 14 — производительность с реальными замерами до/после.
Ключевые цифры:
- Sales-report SQL: 9.53ms → 7.09ms mean (-25%) после N+1 fix +
индексов.
- Initial JS bundle: 1456 KB → 706 KB raw (-51%); gzip 389 KB →
196 KB (-50%) через React.lazy на 30 редких страниц + Recharts.
- Lighthouse /login: Perf 89, A11y 92, BP 100 (target ≥85/90/90 ✓).
Подробности по каждому пункту + методология замеров — в
docs/sprint14-progress.md.
Что сделано:
1. Phase14a_PerfIndexes — composite (Org,Status,Date), partial
(WHERE Status=1 AND NOT IsReturn) + INCLUDE, и composite
stock_movements(Org,OccurredAt).
2. SalesReportController.FetchAsync — раньше каждая строка
результата делала CASE WHEN ELSE (SELECT ... LIMIT 1)
correlated subquery на RetailPoint.Name и User.FullName.
Заменено на 2 IN-batch'a + dictionary lookup в C#.
3. App.tsx React.lazy для отчётов, audit-log, loyalty, super-admin,
settings, all rare edit pages. Recharts вынесен в lazy chunk
Dashboard'а (KPI рендерятся сразу).
4. SixLabors.ImageSharp v3.1.6 + ImageVariantService — генерирует
thumb 256/medium 800 WebP@80 при загрузке. UploadsController
?size=thumb|medium с fallback. React <ProductImage> — <picture>
+ srcset.
5. ApplyDefaultPoolConfig на старте: Max=100, Min=10 (грей пул),
Idle=300, Max Auto Prepare=20.
6. Lighthouse на /login /forgot-password /reset-password —
все три проходят пороги.
7. JobTimingFilter + HangfireGlobalFilterRegistrar — каждый
recurring job логирует длительность; >30s = Warning.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>