Some checks failed
Auto-tag / Create date-tag (push) Has been cancelled
CI / Backend (.NET 8) (push) Has been cancelled
CI / Web (React + Vite) (push) Has been cancelled
CI / POS (WPF, Windows) (push) Has been cancelled
Docker API / Build + push API (push) Has been cancelled
Docker API / Deploy API on stage (push) Has been cancelled
Старый idempotency-маркер — Product с Article LIKE 'DEMO-%'. После того как «Демо-магазин» на стенде наполнили реальным каталогом из МойСклада (артикулы не DEMO-*), маркер срабатывал false и QuickLoginDemoSeeder при каждом рестарте стенда пытался залить фиктивный каталог заново — падая на unique-индексе IX_product_barcodes_OrganizationId_Code (23505). В БД ничего лишнего не попадало (транзакция откатывалась), но лог stage-api получал 9 ERR в сутки. Фикс: SeedAsync и PeekAsync считают «уже сидировано» при любом Product в орге. Поведение из UI (POST /api/admin/seed-demo) не меняется для свежих org'ов. Прочие стартовые сидеры (OpenIddictClientSeeder, SystemReferenceSeeder, DevDataSeeder) и HangfireJobsConfigurator — уже идемпотентны, аудит в docs/seeder-noise-progress.md. Integration-тест: - SeedDemo_second_call_on_populated_org_returns_already_seeded - SeedDemo_on_org_with_imported_catalog_skips_without_duplicate_barcode Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
51 lines
3.8 KiB
Markdown
51 lines
3.8 KiB
Markdown
# Seeder noise cleanup — прогресс
|
||
|
||
Контекст: при каждом старте `food-market-stage-api` в логах появлялись `ERR`
|
||
от `QuickLoginDemoSeeder → DemoTenantSeeder.SeedAsync` — повторная попытка
|
||
залить фиктивный DEMO-каталог в «Демо-магазин», падающая на unique-индексе
|
||
`IX_product_barcodes_OrganizationId_Code` (23505). В БД ничего лишнего не
|
||
попадало (транзакция откатывалась), но лог был зашумлён ~9 строк/сутки.
|
||
|
||
Корневая причина: idempotency-маркер `DemoTenantSeeder` — наличие Product
|
||
с `Article LIKE 'DEMO-%'`. После того как «Демо-магазин» наполнили настоящим
|
||
каталогом из МойСклада (у товаров есть `MoySkladExternalId`, а артикулы
|
||
не начинаются с `DEMO-`), маркер срабатывал false → сидер шёл заново,
|
||
генерировал новые Product'ы с теми же EAN-13 (seed 200_000_000_001…), и
|
||
упирался в unique по `(OrganizationId, Code)` на первом же `SaveChangesAsync`.
|
||
|
||
## Чеклист
|
||
|
||
- [x] 1. `DemoTenantSeeder.SeedAsync` ничего не делает, если в организации уже
|
||
есть любые товары (а не только `DEMO-*`). Демо-магазин с реальным
|
||
каталогом из МойСклада не трогаем.
|
||
- [x] 2. Остальные стартовые сидеры/джобы проверены — нет повторных вставок.
|
||
- [x] 3. Integration-тест: повторный запуск сидера на организации с товарами
|
||
не пишет и не падает.
|
||
- [x] 4. `~/deploy-stage.sh`, логи stage-api после старта — 0 строк `ERR`/`fail`.
|
||
|
||
## Что меняем
|
||
|
||
1. В `DemoTenantSeeder.SeedAsync` idempotency check расширяется: сейчас
|
||
«уже сидировано» = Product с `Article LIKE 'DEMO-%'`. Новая логика —
|
||
«уже сидировано» = любой Product в орге (`_db.Products.AnyAsync(p ⇒
|
||
p.OrganizationId == orgId)`). При таком раскладе `CountExistingAsync`
|
||
вернёт реальные count'ы, `AlreadySeeded = true`, никаких вставок.
|
||
2. Поведение из UI (`POST /api/admin/seed-demo`) не меняется для свежих
|
||
org'ов. Для орги с товарами возвращаем summary без вставок — ровно
|
||
как и раньше, только маркер стал шире.
|
||
3. `YearDemoSeeder` уже и так защищён `hasActivity` гардом (Supply или
|
||
RetailSale) — к нему претензий нет.
|
||
|
||
## Какие сидеры/джобы проверены
|
||
|
||
| Hosted service | Повторные вставки при каждом старте? |
|
||
|---|---|
|
||
| `OpenIddictClientSeeder` | нет — проверяет `FindByClientIdAsync` + update |
|
||
| `SystemReferenceSeeder` | нет — все вставки через `AnyAsync` / `FirstOrDefault` guard |
|
||
| `DevDataSeeder` | нет — все вставки идемпотентны (роли/юзеры/единицы/org/stores) |
|
||
| `QuickLoginDemoSeeder` | один путь (`TryFillCatalogAsync`) чинится в задаче — он здесь корень шума |
|
||
| `HangfireJobsConfigurator` | нет — `AddOrUpdate` по cron-id |
|
||
| `HangfireGlobalFilterRegistrar` | нет — pure в-процесс конфиг |
|
||
| `InvoiceWorker` | polling-воркер, не сидер |
|
||
| `ReferencePriceRefreshJob` | polling-воркер, не сидер |
|