food-market/docs/seeder-noise-progress.md
nns 99bf6c9f3e
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
fix(seed): DemoTenantSeeder гардит по наличию ЛЮБЫХ товаров в орге
Старый 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>
2026-10-06 00:50:50 +05:00

3.8 KiB
Raw Permalink Blame History

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.

Чеклист

  • 1. DemoTenantSeeder.SeedAsync ничего не делает, если в организации уже есть любые товары (а не только DEMO-*). Демо-магазин с реальным каталогом из МойСклада не трогаем.
  • 2. Остальные стартовые сидеры/джобы проверены — нет повторных вставок.
  • 3. Integration-тест: повторный запуск сидера на организации с товарами не пишет и не падает.
  • 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-воркер, не сидер