Stock — operator runbook
Операционное руководство для P2b StockRule aggregate (см. ADR-0037).
Feature flags
Engine + admin HTTP + readiness probe — always on. P1 stub retired в commit d6130fb. Master flags нет.
Env vars
| Var | Default | Tuning |
|---|---|---|
STOCK_SNAPSHOT_POLL_INTERVAL | 10s | Incremental fetch cadence. Поднять до 30s под write-heavy load. |
STOCK_SNAPSHOT_FULL_RELOAD_INTERVAL | 5m | Полный rebuild snapshot — safety net против drift. |
JWT admin role: stock_admin (отдельная permission boundary от pricing_admin).
Readiness
stock.snapshot health.Checker fails until PollingLoader.Bootstrap completes. /readyz возвращает 503 с component=stock.snapshot до окончания первой загрузки. После Bootstrap success — green; reload failures не flip checker (ловятся через метрику).
Метрики (counters)
Все counters пишутся в slog (stock.metrics.* events) до подключения OTel exporter.
| Metric | Labels | Source |
|---|---|---|
stock_snapshot_reload_total | mode ∈ {incremental, full} × result ∈ {ok, failed} | PollingLoader.tick / fullReload |
stock_outbox_emit_total | event_kind × result | CrudService.runInTxAndEmit |
stock_overlap_conflicts_total | — | OverlapValidator.Check (CrudService Create/Update) |
stock_rule_mutations_total | event_kind × result | CrudService post-commit |
Alert примеры:
rate(stock_snapshot_reload_total{result="failed"}[5m]) > 0— DB unreachable, P1.rate(stock_overlap_conflicts_total[1h]) > N(тюнинг по нагрузке) — operators ставят пересекающиеся правила, P3.rate(stock_outbox_emit_total{result="failed"}[5m]) > 0— outbox tx-level errors, P2.
Rule kinds
4 transformation типа (см. ADR-0037 §3 + spec):
| Kind | Что делает |
|---|---|
visibility_filter | Drops/excludes warehouses по identity (kinds/codes) ИЛИ logistics SLA (MaxLeadTimeDays). Mode: hide или exclude_from_total. |
threshold | Минимум qty чтобы considered «available»; optionally restricted to qty within MaxLeadTimeDays. Action: reject или partial. |
display_transform | Round_down (кратное N), bucket (диапазон + label), hide_exact (in_stock/out_of_stock без числа). |
synthetic_fallback | Когда total qty == 0: показать «on_request» или synthetic_qty + опциональный SyntheticLeadDays (feeds Delivery layer). |
Engine применяет в canonical порядке: filter → threshold → synthetic → display. Predictable вне зависимости от operator priority.
Triage
| Симптом | Шаг |
|---|---|
/readyz красный, причина stock.snapshot | Проверить PG. Если PG жив — глянуть stock.snapshot.bootstrap slog event + pg_stat_activity long queries. |
Шквал stock.metrics.overlap_conflict | Operators ставят пересекающиеся правила. Глянуть StockRuleConflictDetected outbox events для anchor IDs. |
| Operator: «правило не применяется» | Snapshot lag до STOCK_SNAPSHOT_POLL_INTERVAL (default 10s) — норма. Глянуть stock.snapshot.reload events на drift. |
display_label не совпадает с available_qty в response | By design: available_qty = exact (downstream Delivery), display_label = formatted для UI. Не bug. |
| Warehouse с unknown lead_time отфильтрован visibility_filter | By design: MaxLeadTimeDays без явного lead_time treats as ∞ → drops. Operator должен расширить filter если хочет. |
Связано
- ADR-0037 — Stock Rule Aggregate
- OpenAPI: stock-admin.yaml
- Spec: P2b Stock Rule Aggregate Design
- Pricing operator runbook — sister BC, similar pattern.