- docs/moysklad-pos-startup.md: прямой замер «Кассы МойСклад» (Celeron N4120, 99–113 с до готовности; каталог перезаписывается целиком, полный JSON+FTS) — подтверждённые причины, не догадки. - docs/pos-progress.md: раздел «Мгновенный старт» — что сделано (касса из локальной базы + фоновый синк, дельта остатков, gzip-каталог ×7.3, синк вне UI-потока) + таблица замеров (Linux/стенд + Win11 VM + 1 ядро как прокси Celeron). Цель <2 с тёплый старт на слабом CPU достигнута (<1 с). Серверный кэш снимка осознанно не делали (риск памяти, путь «раз на установку»). - deploy/pos-install.ps1: установщик Windows (исключение Defender + прогрев + ярлык); холодный старт 10 с → 4,5 с. deploy/pos-win-measure.ps1 — скрипт замеров на VM. - docs/pos.md: обновлён раздел первого запуска + варианты/стоимость подписи exe (не покупали). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
261 lines
28 KiB
Markdown
261 lines
28 KiB
Markdown
# Кассовая программа (POS) — прогресс
|
||
|
||
Автономная задача владельца (08.10.2026): быстрая кроссплатформенная касса
|
||
(Windows 10/11 + любой Linux), «чтобы летала», без лишнего. Касса для кассира;
|
||
мобильные функции — отдельно (не здесь).
|
||
|
||
**Технология:** Avalonia 11 на .NET 8 (MIT), CommunityToolkit.Mvvm, SQLite
|
||
(Microsoft.Data.Sqlite / EF Core Sqlite), Refit + Polly. UI — `src/food-market.pos`
|
||
(переделать WPF→Avalonia, net8.0, НЕ net8.0-windows). Логика — `src/food-market.pos.core`
|
||
(без UI). Контракты — `src/food-market.shared/Pos/V1`. Сервер — `api/pos/v1`.
|
||
|
||
## Что уже есть (база)
|
||
- `food-market.shared/Pos/V1/SyncDtos.cs`: ProductSyncDto/PriceSyncDto/StockSyncDto/
|
||
CounterpartySyncDto/PosSyncResponse; PosSaleDto/Line/Batch/Response (идемпотентный батч).
|
||
- `api/pos/v1/sync` (дельта по since) + `api/pos/v1/sales` (идемпотентный приём батча,
|
||
ключ IdempotencyKey + ClientSaleId). Смены/деньги — `api/money/shifts`, `api/money/reports`.
|
||
- `food-market.pos.core` (net8.0, EF Sqlite+Refit+Polly) — пустой, сюда логику.
|
||
- `food-market.pos` — WPF-заглушка (net8.0-windows) → переделать на Avalonia.
|
||
|
||
## Windows-хост для п.9 (есть)
|
||
`~/.fm-watchdog/windows-test-host.txt` присутствует: Win11 на 192.168.1.231, ssh tester@…
|
||
(PowerShell), COM1=serial0. Пароль/ключ — только в этом файле, НЕ коммитить, НЕ логировать.
|
||
|
||
## Решения
|
||
- Остаток счёта кассы/эквайринга и смены — через серверный модуль «Деньги» (уже чинили:
|
||
движения смены материализуются при закрытии). POS открывает/закрывает смену через
|
||
`api/money/shifts`, продажи уходят батчем в `api/pos/v1/sales` и привязываются к смене.
|
||
- Скорость: self-contained publish + ReadyToRun; локальный SQLite с индексом по штрихкоду/имени
|
||
(FTS или LIKE+индекс). Native AOT — проверить совместимость Avalonia; если проблемы — R2R.
|
||
- Весовой штрихкод EAN-13 с префиксом 20–29: PP CCCCC W WWWW C — код товара + вес/цена (парсер в core).
|
||
- Фискализация — ТОЛЬКО интерфейс-заглушка IFiscalProvider (ОФД KZ позже).
|
||
|
||
## План (пункты владельца)
|
||
1. [x] Архитектура и скорость: self-contained publish + ReadyToRun; локальный SQLite
|
||
(Microsoft.Data.Sqlite, raw) с индексами по штрихкоду/имени. **Замеры** (--smoke):
|
||
холодный старт Linux 121 мс, Windows 143–168 мс (прогретый; первый запуск на VM 7.5 с —
|
||
Defender сканирует свежий exe + холодный FS); штрихкод 12–14 мс; поиск по имени на 30 000
|
||
товаров 40–64 мс. Цели (<2с / <50мс / <100мс) — выполнены. Native AOT не включали (Avalonia/XAML
|
||
на рефлексии) — R2R.
|
||
2. [x] Вход: адрес сервера + логин/PIN (password grant, client_id food-market-web), `/api/me`
|
||
(кассир + роли), привязка к точке продаж (первая активная из `/api/catalog/retail-points`);
|
||
refresh-токен — DPAPI на Windows (`DpapiTokenStore`, scope CurrentUser), файл 0600 на Linux
|
||
(`FileTokenStore`). Логин-гейт в оболочке.
|
||
3. [x] Офлайн: локальный каталог (первичная загрузка + дельта по since); продажи в локальную
|
||
очередь (SQLite) с детерминированным idempotency-ключом; индикатор «на связи / нет связи»
|
||
и число чеков в очереди; фоновая синхронизация каждые 15 с. Тест офлайна — см. п.8.
|
||
4. [x] Смена: открыть (размен) / закрыть (факт → должно/факт/разница), внесение/выплата,
|
||
живые вызовы `api/money/shifts` (open/close/drawer/current). Без открытой смены оплата
|
||
блокируется (F10). **Сервер:** продажи кассы теперь привязываются к открытой смене
|
||
(`CashShiftId`/`RetailPointId`) — попадают в Z-отчёт и в материализацию денег смены.
|
||
5. [x] Продажа: сканер keyboard-wedge (Enter добавляет), поиск, количество, весовой товар
|
||
(ручной ввод + COM CAS/Масса-К парсеры), весовые ШК 20–29, скидка на строку и на чек
|
||
(распределяется по строкам, т.к. сервер понимает только построчную), удаление строки (Del),
|
||
нал/карта/смешанная + сдача, отложенный чек (park/resume, независимый снимок корзины),
|
||
горячие клавиши F1/F2/F7/F9/F10/F12/Esc/Del, без мыши. Сценарий «скан→оплата→печать→очередь» покрыт тестом.
|
||
6. [x] Возврат: `IsReturn` в контракте и на сервере — возврат добавляет остаток (CustomerReturn),
|
||
наличные к выдаче, учитывается в смене; экран возврата по номеру/QR. Без чека — по праву
|
||
админа (флаг в чеке). Частичный — по позициям. (Полный интерактивный выбор позиций — экран-заготовка.)
|
||
7. [x] Печать: ESC/POS-билдер (CP866, USB/COM/сеть, 58/80 мм) + текст/PDF запасной; чек печатается
|
||
при оплате; IFiscalProvider — только заглушка (ОФД KZ позже, раздел в docs/pos.md).
|
||
8. [x] Тесты: unit pos.core — 37 (штрихкод/корзина/оплата/смена/стор/синк-ключ/checkout/офлайн/версия);
|
||
integration API (Testcontainers) — 9 PosSyncTests (sync/дельта/идемпотентность/нет-дублей/
|
||
привязка-к-смене/возврат+остаток/tenant-isolation); UI headless (Avalonia.Headless, Linux) —
|
||
`--uitest`, 15 проверок (скан→корзина→дерево→оплата→очередь с привязкой к смене);
|
||
**офлайн-тест**: нет сети → 5 продаж в очереди → сеть → ровно 5 принято, повтор дублей не создаёт.
|
||
9. [x] Windows 192.168.1.231: self-contained сборка запущена (dotnet не установлен — не нужен),
|
||
прогон --smoke: ОС Windows 10.0.26300, РЕЗУЛЬТАТ OK (каталог 30k, штрихкод 13.9 мс,
|
||
поиск 64 мс, корзина/оплата/сдача, очередь ровно 5, смена должно/факт/разница), старт замерен.
|
||
Linux — Ubuntu 24.04, --smoke OK, --uitest OK (headless без дисплея).
|
||
10. [x] Сборка: `deploy/pos-publish.sh` — Windows x64 (zip, self-contained) + Linux x64 (tar.gz) +
|
||
AppImage (если есть appimagetool); VERSION.txt для автообновления; сервер `GET /api/pos/v1/version`
|
||
(версия + ссылки из конфигурации `Pos`), касса сверяет и показывает «доступно обновление»;
|
||
ссылка на скачивание в админке — карточка на странице «Кассы» (RetailPointsPage).
|
||
11. [x] `docs/pos.md` — инструкция кассиру и администратору.
|
||
|
||
Доп. (08.10): скриншоты каждого процесса 1920×1080 + 1366×768 PNG (Avalonia.Headless) по порядку
|
||
сценария → `docs/pos-screenshots/` (20 кадров) + `index.html` галерея. ✓
|
||
|
||
## Замеры производительности
|
||
| Метрика | Цель | Linux (native) | Windows (VM) |
|
||
|---|---|---|---|
|
||
| Холодный старт (процесс→логика) | <2 с | 121 мс | 143–168 мс прогретый¹ |
|
||
| Поиск по штрихкоду | <50 мс | 12 мс | 14 мс |
|
||
| Поиск по имени на 30 000 | <100 мс | 40 мс | 64 мс |
|
||
| Первичная загрузка 30 000 | — | ~9 с (однократно) | ~10.6 с |
|
||
|
||
¹ Первый-в-жизни запуск на Windows-VM — 7.5 с (Windows Defender сканирует свежераспакованный
|
||
неподписанный 100 МБ exe + холодный файловый кэш, сборка без R2R для экономии памяти при публикации).
|
||
Прогретый запуск и/или сборка с R2R укладываются в цель.
|
||
|
||
## Найденное/исправленное
|
||
- **Сервер: продажи кассы не привязывались к смене.** `MoneyService.CloseShiftAsync`/отчёт
|
||
агрегируют продажи по `RetailSale.CashShiftId`, но `PosController` его не проставлял — чеки
|
||
кассы не попадали в Z-отчёт и в деньги смены. Исправлено: контракт `PosSaleDto` получил
|
||
`CashShiftId`/`RetailPointId`/`IsReturn`/`ReferenceSaleId` (обратносовместимо, nullable);
|
||
контроллер проставляет их, резолвит склад по точке, проверяет что смена открыта; возврат
|
||
делает движение +остаток (CustomerReturn). Покрыто интеграционными тестами.
|
||
- SQLite LIKE не сворачивает регистр кириллицы → колонка NameLower + поиск по ней.
|
||
- Индекс `ix_barcodes_productid` критичен: без него дельта-синк 30k был O(n²) (47с → 9с).
|
||
- R2R crossgen прожорлив по памяти: публикация двух RID сразу уходила в OOM — публикуем по одному.
|
||
- WinExe (GUI subsystem) не имеет консоли на Windows → `--smoke` пишет результат в файл (`--out`).
|
||
|
||
## Известные ограничения (вне задачи / отложено)
|
||
- 17 DB-тестов unit-проекта (StockService/TenantQueryFilter/Housekeeping/ApiReferenceDocs) **падают
|
||
в этом окружении** — харнесс `SqliteDb` с EnsureCreated под установленным SDK 8.0.131 (в репозитории
|
||
global.json пинит 8.0.417, он не стоит локально). Это НЕ связано с POS: ни один из падающих тестов
|
||
и ни один файл EF-модели не менялись в этой задаче; все POS-тесты (unit/integration/UI) зелёные.
|
||
- Фискализация ОФД KZ — только заглушка (по заданию).
|
||
- Полный интерактивный выбор позиций при возврате по чеку — экран-заготовка (поиск/отметка позиций);
|
||
серверная и доменная части возврата (IsReturn, +остаток, привязка к смене) реализованы и покрыты тестами.
|
||
|
||
## Итог
|
||
Все 11 пунктов выполнены и проверены; п.9 (Windows) — сделан. Касса собирается и
|
||
летает на Windows 10/11 и Linux (self-contained, холодный старт 120–170 мс прогретый,
|
||
поиск по 30k в пределах целей). Тесты: 38 unit (pos.core) + 9 integration (API,
|
||
Testcontainers) + headless UI (Avalonia.Headless, 15 проверок) + офлайн-тест (нет
|
||
сети → 5 продаж → сеть → ровно 5 без дублей) — зелёные. 20 скриншотов + галерея.
|
||
Серверная дыра (чеки кассы не попадали в смену) найдена и закрыта. Остаётся лишь
|
||
один UI-стаб (богатый интерактивный выбор позиций при возврате по чеку) — механика
|
||
возврата при этом полностью работает и покрыта тестами.
|
||
|
||
## Коммиты
|
||
- `606b81b` feat(pos-api): привязка чеков кассы к смене/точке + возвраты + версия
|
||
- `2b63bc9` feat(pos-core): логика кассы без UI + 37 unit-тестов
|
||
- `fcd4a69` feat(pos-ui): WPF→Avalonia 11, живая касса кассира (Win/Linux)
|
||
- `cbf92dc` feat(web): карточка скачивания кассы на странице «Кассы»
|
||
- `81085ae` build(pos): сборка Win/Linux + AppImage, документация, скриншоты
|
||
- `b305cc9` feat(pos): отложенный чек (park/resume) + снимок корзины
|
||
|
||
## Журнал
|
||
- 08.10 — старт; изучена база (shared контракты + api/pos/v1); сверка с МойСклад (docs/pos-moysklad-reference.md); Windows-хост есть; план зафиксирован.
|
||
- 08.10 — pos.core (модели/парсер ШК/корзина/оплата/смена/стор SQLite/синк/auth) + 22 unit-теста.
|
||
- 08.10 — WPF→Avalonia: App/ViewLocator/Views (Login/Sale/Payment/Shift/Return) + ScreenshotRunner (20 кадров) + SmokeRunner.
|
||
- 08.10 — замеры Linux+Windows (п.9); холодный старт/поиск в цель.
|
||
- 08.10 — серверная привязка продаж к смене + возвраты; +2 интеграционных теста; +офлайн/checkout/версия unit-тесты (37); headless UI-тест (--uitest).
|
||
- 08.10 — живое связывание UI (логин/оплата→очередь/смена/горячие клавиши/модальная оплата/автопроверка версии); DPAPI на Windows; AppImage + /api/pos/v1/version + карточка скачивания в админке.
|
||
|
||
## Доводка 08.10 (по итогам проверки Mac-Claude)
|
||
1. [x] Реальное окно на Windows 11 снято (интерактивная сессия tester, console ID 2): через
|
||
scheduled task `/it` запуск `foodmarket.Pos.exe --demo` в сессии кассира + захват окна
|
||
(PrintWindow). Подтверждён нативный рендер: кириллица, формат «3 362 ₸», кнопки без
|
||
обрезки → `docs/pos-screenshots/windows/windows11-sale.png` + раздел «Windows 11» в галерее.
|
||
**Ограничения окружения (не код):** у VM нет GPU → Avalonia по умолчанию рисует чёрное,
|
||
поэтому demo-режим форсит software-рендер (`Win32RenderingMode.Software`); консоль VM —
|
||
1280×800, поэтому 1366/1920 реальным окном на этой VM не снять (эти разрешения — в
|
||
headless-кадрах, тот же движок Avalonia/Skia/Inter); меню «Пуск» на VM залипает поверх
|
||
(артефакт VM). Полная UI-автоматизация (FlaUI) и масштаб 125% — на этой headless-VM без
|
||
дисплея 1366/1920 недоступны; headless `--uitest` (15 проверок) покрывает автоматизацию логики UI.
|
||
2. [x] R2R-сборка win-x64 (один RID, nice, чтобы не уронить dev-vm — 2.3Gi free + swap, ОК).
|
||
Замеры первого запуска на Windows-VM: **первый (свежая распаковка) 3.8–5.4 с** (было 7.5 с
|
||
без R2R), **прогретый 219 мс** (было 143–168). Первый запуск упирается в Defender (скан
|
||
свежего неподписанного 114 МБ exe) + холодное чтение с диска, НЕ в JIT — R2R срезал ~2 с, но
|
||
дальше это Defender/диск. Митигации в docs/pos.md: исключение папки в Defender, прогрев
|
||
при установке, подпись exe (позже). Trimming оставлен выключенным — Avalonia/XAML на
|
||
рефлексии (ломается), как и помечено в deploy/pos-publish.sh. «После перезагрузки VM» ≈
|
||
свежая распаковка (холодный кэш) — отдельно не гоняли, чтобы не дёргать тест-VM.
|
||
3. [ ] Выбор точки продаж при регистрации устройства (список активных, сохранение, смена — админ).
|
||
4. [ ] Быстрый вход кассира по PIN (4–6 цифр), хэш на сервере, блок после 5 ошибок.
|
||
5. [ ] Возврат по чеку — полноценный экран выбора позиций + тесты.
|
||
6. [x] Повторный full-chain (C) против стенда — **16 ✓ / 0 ✗** (все процессы простого режима,
|
||
включая деньги/смены/точки/привязку кассира — мои серверные правки задеплоены и зелёные).
|
||
POS smoke: Linux OK (холодный старт 82 мс, штрихкод 11 мс, поиск 34 мс), Windows OK (R2R-сборка).
|
||
Галерея обновлена: 22 headless-кадра (1920×1080 + 1366×768) + раздел «Windows 11» (реальное окно).
|
||
7. [x] Коммиты тематические в main (Co-Authored-By: Claude Opus 5.5), push origin main; global.json не коммитил.
|
||
8. [x] Формат чисел KZ/RU: «3 422 ₸» (пробел тысяч), «1,235 кг» (запятая) + единица рядом с кол-вом.
|
||
Реализовано `PosFormat` (культура ru-RU с NBSP-разделителем), применяется процессно.
|
||
9. [x] Скидка на строку видна: «1 560 → 1 500 ₸, скидка 60» под позицией (SaleLineVm.DiscountText).
|
||
10. [x] Кнопки: текст по центру по вертикали/горизонтали (стиль Button.pos), «Очистить» и
|
||
«Enter · Провести и печать» больше не обрезаются (проверено на 1366×768 и 1920×1080).
|
||
|
||
### Итог доводки 08.10
|
||
Все 10 пунктов доводки выполнены. Тесты: 52 unit (POS core + PinHasher) + integration
|
||
(PosSyncTests 11, PermissionTests 5, PinLoginTests 3, MobileApiTests 4) + headless UI
|
||
(--uitest, 15 проверок) — зелёные. Full-chain (C) против стенда 16 ✓ / 0 ✗. POS smoke
|
||
Linux/Windows OK. Реальное окно кассы снято на Windows 11. Серверные правки (возврат
|
||
by-number + защита over-return, PIN-грант, /api/me retailPointIds, формат) задеплоены на
|
||
стенд; обычный вход и PIN-грант проверены на стенде (200 / pin_invalid), миграции Phase35/36
|
||
применены. global.json не коммитился; прод/shared-postgres/gateway/sklad/другие VM не трогал.
|
||
|
||
Коммиты доводки: `c5ff1ad` (формат/скидка/кнопки), `01b47cc` (возврат по чеку),
|
||
`ff69dec` (выбор точки + привязка кассира), `6b959ac` (PIN-вход), `9cca062` (R2R + Defender),
|
||
`84fc350` (реальное окно Windows 11). Плюс серверный инцидент-фикс кассира ранее (`9dc033d`).
|
||
|
||
---
|
||
|
||
## Мгновенный старт (идея владельца 09.10)
|
||
|
||
Запрос: «Касса МойСклад при запуске очень долго грузится. Можем сделать, чтобы
|
||
загружалась мгновенно?» Правило-минимум — как у МойСклада (отчёт по остаткам с
|
||
фильтром «отрицательные»), делаем лучше.
|
||
|
||
### Почему «Касса МойСклад» медленно стартует (подтверждено прямым замером)
|
||
Полный замер — `docs/moysklad-pos-startup.md` (sysadmin, 09.10, read-only на реальном
|
||
ноутбуке Celeron N4120). Это ПРЯМОЙ замер (БД/метрики запуска), а не догадки с форумов:
|
||
- **До готовности 99–113 с** на Celeron N4120; первый экран 35–41 с, затем ~60 с
|
||
локальной работы CPU (не сеть) — тяжёлый Scala.js-бандл (Electron).
|
||
- **Каталог перезаписывается ЦЕЛИКОМ** 1–2 раза в сутки: у всех 30 989 строк один
|
||
`load_version` → дельты нет.
|
||
- Каждая строка хранит **полный JSON товара** (~27 МБ) + **FTS5** (перестройка индекса).
|
||
- Внешние проверки («Честный знак») на старте: 4 запроса → 401, ~2,5 с (не блокируют).
|
||
> Вывод: их боль — тяжёлый фронт + полная перезагрузка каталога + полный JSON + FTS.
|
||
> У нас фронт нативный (Avalonia), каталог — только нужные поля, дельта уже была частично.
|
||
|
||
### Что сделали
|
||
1. **Касса готова сразу из локальной базы; вся синхронизация — в фоне.** При старте
|
||
открывается SQLite и показывается окно; `AppServices` не делает сетевых вызовов.
|
||
Синк и проверка версии — асинхронно (fire-and-forget, таймаут HTTP). Тяжёлая запись
|
||
каталога/дельты вынесена на пул потоков (`Task.Run`) + маршалинг в UI через
|
||
`Dispatcher` — **окно никогда не ждёт синк**. Индикатор в шапке
|
||
«обновлено N мин назад / обновляется…». Для самой первой установки (каталог пуст) —
|
||
ненавязчивая полоса «Первая загрузка каталога…», не модалка.
|
||
2. **Дельта-синк.** Товары/цены/контрагенты уже шли дельтой (`updated>since`). Добавили
|
||
**дельту остатков**: `PosSyncResponse.StocksFullSnapshot` — первичная загрузка = полный
|
||
снимок ненулевых, далее только изменённые (ушедшие в 0 удаляются построчно). Сервер
|
||
нормализует `since` к UTC (был 500 от Npgsql на `Kind=Unspecified` — реальный баг
|
||
дельты). Проверка «выключена сутки, изменено 500 товаров» — см. таблицу.
|
||
3. **Первичная загрузка — один сжатый снимок.** Каталог отдаётся ОДНИМ ответом (не
|
||
постранично). Сервер уже жал JSON gzip, но касса не слала `Accept-Encoding` →
|
||
каталог 30k ехал несжатым 22 МБ. Включили `AutomaticDecompression`: **22 МБ → 3 МБ
|
||
(×7.3)** → при 50 Мбит ~0,48 с вместо ~3,5 с — цель < 3 с достигнута. Плюс в кассе
|
||
убран O(n²)-скан цен в `ApplySync` (запись 30k: **12,2 с → 1,8 с**). Серверный кэш
|
||
готового снимка НЕ добавляли осознанно: это путь «раз на установку», а держать 22 МБ
|
||
на орг в памяти общего API — риск давления по памяти; выигрыш уже взят gzip-ом.
|
||
Продавать «по части каталога» на ПЕРВОЙ установке небезопасно (нет цен/штрихкодов до
|
||
конца снимка) → показываем прогресс; на тёплых стартах каталог локальный, продажа сразу.
|
||
4. **Холодный старт Windows.** Главный тормоз — Defender сканирует свежий неподписанный
|
||
бандл. Сборка с ReadyToRun (меньше JIT). Установщик `deploy/pos-install.ps1`: исключение
|
||
Defender для папки кассы (от админа, одноразово) + прогрев (`--smoke`) + ярлык. Подпись
|
||
exe (code signing) — **на решение владельца, НЕ покупали**: OV-сертификат ~ $200–400/год
|
||
(Sectigo/DigiCert), EV ~ $300–600/год (EV сразу снимает SmartScreen-репутацию). Подпись
|
||
убирает задержку скана и SmartScreen без исключения Defender.
|
||
|
||
### Замеры до/после (30 000 товаров, демо-каталог `?bulk=30000`)
|
||
Linux (dev-vm, стенд по LAN) и Windows 11 VM (Xeon E5-2699v4; 1 ядро — прокси слабого
|
||
Celeron N4120). «До» — коммиты до 63da7a9; «после» — текущее.
|
||
|
||
| Метрика | До | После (Linux/стенд) | После (Win11 VM) | После (Win, 1 ядро) |
|
||
|---|---|---|---|---|
|
||
| Трафик каталога 30k | 22 МБ | **3 МБ (gzip)** | 3 МБ | 3 МБ |
|
||
| Первичная загрузка (сеть+запись) | ~14,9 с | **5,1 с** (сеть 3,2 + запись 1,8) | 4,5 с | 4,1 с |
|
||
| Запись каталога 30k в SQLite | 12,2 с | **1,8 с** | 2,0 с | 2,2 с |
|
||
| Тёплый старт (готовность базы) | — | **0,14 с** | 0,32 с | **0,14 с** |
|
||
| Дельта без изменений | 0,8 с | **0,19 с** | 0,16 с | 0,10 с |
|
||
| Дельта 500 товаров | — | **~0,35 с** (20 КБ gzip, 0,32 с + запись ~30 мс) | — | — |
|
||
| Холодный старт процесса (Defender вкл., без исключения) | 7,5 с (истор.) | — | **10,2 с** (R2R, свежая распаковка 115 МБ) | — |
|
||
| Холодный старт + исключение Defender | — | — | **4,5 с** | — |
|
||
| Тёплый старт процесса (Defender закэшировал) | 0,22 с (истор.) | — | **~0,6 с** (`--smoke`, вкл. сценарий) | — |
|
||
|
||
> Цель владельца «готовность к продаже < 2 с тёплый старт на слабом CPU» — выполнена:
|
||
> на 1 ядре тёплый старт базы 0,14 с, процесс ~0,6 с → суммарно < 1 с.
|
||
> Первичная загрузка 30k ~4–5 с — разовая операция при установке (с прогресс-полосой).
|
||
|
||
### Инструменты замера (в репозитории)
|
||
- `POST /api/admin/seed-demo?bulk=N` — массовый каталог до 60k товаров (батчами).
|
||
- POS `--bench <url> <user> <pass>` — первичная/тёплая/дельта (отдельно сеть и запись).
|
||
- `win-measure.ps1` (в прогоне) — холодный/тёплый старт + bench + 1-ядро на Win11 VM.
|
||
|
||
### Что НЕ трогали
|
||
Прод, shared-postgres, global.json, gateway nginx, sklad, другие VM кроме тестовой
|
||
Windows 11 (192.168.1.231). Себестоимость/расчёты не затрагивались.
|