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

VarDefaultTuning
STOCK_SNAPSHOT_POLL_INTERVAL10sIncremental fetch cadence. Поднять до 30s под write-heavy load.
STOCK_SNAPSHOT_FULL_RELOAD_INTERVAL5mПолный 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.

MetricLabelsSource
stock_snapshot_reload_totalmode ∈ {incremental, full} × result ∈ {ok, failed}PollingLoader.tick / fullReload
stock_outbox_emit_totalevent_kind × resultCrudService.runInTxAndEmit
stock_overlap_conflicts_totalOverlapValidator.Check (CrudService Create/Update)
stock_rule_mutations_totalevent_kind × resultCrudService 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_filterDrops/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_transformRound_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_conflictOperators ставят пересекающиеся правила. Глянуть StockRuleConflictDetected outbox events для anchor IDs.
Operator: «правило не применяется»Snapshot lag до STOCK_SNAPSHOT_POLL_INTERVAL (default 10s) — норма. Глянуть stock.snapshot.reload events на drift.
display_label не совпадает с available_qty в responseBy design: available_qty = exact (downstream Delivery), display_label = formatted для UI. Не bug.
Warehouse с unknown lead_time отфильтрован visibility_filterBy design: MaxLeadTimeDays без явного lead_time treats as ∞ → drops. Operator должен расширить filter если хочет.

Связано