Skip to content

Backlog #143 — ws-strict-fail-fast-resync ​

Status: план Component: C-05 (WS Market channel) Severity: High Priority: yes Связан: BUG-11 (reconciliation pool — Вариант 3, дополняет этот fix как Вариант 1)

Контекст ​

Пользователь утвердил 2026-05-13 архитектуру WS self-healing = Вариант 1 + Вариант 3:

  • Вариант 1 (этот fix): strict fail-fast WS parser + REST full resync после reconnect
  • Вариант 3 (отдельный fix, BUG-11): periodic reconciliation pool

Альтернативы отвергнуты (см. memory project_ws_self_healing_decision): dual-WS / monotonic seq / hybrid.

Проблема ​

Текущий WS layer допускает silent failures:

  • Parser может пропустить unknown event types через _ => {} или Err(_) => continue без observable signal
  • После reconnect (exponential backoff 1s→30s) подписки повторно отправляются, но REST snapshot НЕ запрашивается → gap window для всех subscribed ассетов
  • Heartbeat реализован (PING 10s, ACTIVITY_TIMEOUT 90s) — этот аспект корректный

Что чинить ​

Шаг A — Audit silent skip-веток в parser ​

Файл: crates/pm-bot/src/io/ws/book.rs

  • Прочитать все match event_type / match payload ветки
  • Заменить silent skip (_ => (), Err(_) => continue, else { return; }) на:
    • В dev/test: panic!("unknown WS event_type: {}", t) (observable failure)
    • В prod: tracing::error!(?payload, "unknown WS event_type"); ws_unknown_event_total.inc() + продолжить (НЕ silent drop)
  • Каждое решение «drop silently vs error+continue» — задокументировать комментарием

Шаг B — REST resync после reconnect ​

Файл: crates/pm-bot/src/io/ws/book.rs (вокруг reconnect_loop / connect_and_run)

  • После успешного reconnect + resubscribe — emit event WsReconnected { subscribed_assets: Vec<String> } в новый channel или вызвать callback
  • В crates/pm-bot/src/application/scanner.rs — на event WsReconnected:
    • Для каждого subscribed asset → clob.fetch_orderbook_rest_with_metadata(asset)
    • Применить через OrderbookStore::update_from_rest_snapshot (ADR-022 4-branch guard сам разрулит race с свежими WS deltas)
  • Метрика: ws_reconnect_total, ws_resync_assets_total, ws_resync_duration_ms

Шаг C — Метрики observability ​

  • ws_unknown_event_total{event_type} — counter (вместо silent drop)
  • ws_parse_errors_total{kind} — counter
  • ws_reconnect_total — counter
  • ws_resync_assets_total — counter
  • ws_resync_duration_ms — histogram

Rule-IDs затрагиваемые ​

Покрывает ​

  • R-WS-RESNAPSHOT-AFTER-RECONNECT (если в strategy.md §9)
  • R-WS-RECONNECT-CASE-DISCONNECT (resync window закрывается)
  • R-WS-RECONNECT-CASE-HALF-OPEN
  • R-WS-RECONNECT-CASE-PING-TIMEOUT
  • R-WS-RECONNECT-EVIDENCE-METRIC (метрики добавлены)
  • Любые правила про fail-fast / observable failure в parser (нужно сверить grep'ом)

Не покрывает (остаются open или для будущих backlog) ​

  • R-WS-GAP-MARKER-DB (запись gap в БД для backtest replay)
  • R-WS-STREAM-SESSION-ID (Polymarket не присылает session ID, sticky-routing невозможен)
  • R-WS-MONOTONIC-INGEST-SEQ (не нужен в V1 — отвергнут пользователем)

Investigation TODO для design phase ​

  • [x] Перечислить ВСЕ silent-skip lines в book.rs (rg + ручная проверка) — 17 локаций (15 в book.rs + 2 в user.rs), таблица в design §2.1
  • [x] Перечислить match в JSON parsing на event_type (там самые опасные silent drops) — диспетчер book.rs:817-829, Class A в design §3.1
  • [x] Сверить с rule-IDs §9 strategy.md — какие точно правила покрывает — 13 полных + 2 частичных, design §5
  • [x] Решить: panic в dev/test vs error+continue в prod — cfg(debug_assertions) compile-time gate (design §3.1 — обоснование против runtime knob)

Design status: ACCEPTED 2026-05-13 — см. docs/specs/ws-strict-fail-fast-resync-design.md. Implementation: pending backend-developer (5 phases, ~315 LOC прод + ~320 LOC тестов).

Альтернативы (отвергнуты, для истории) ​

  • Dual WS + LRU dedup — 2× нагрузка на Polymarket, не защищает от server-side failures (broken payload в обе коннекции одновременно)
  • Monotonic ingest sequence + gap detection — не детектит steady-rate drift (если server отправил неполную серию дельт без time gap'а)
  • Hybrid (escalation на second WS при detected drift) — слишком сложно, окно первого drift всё равно не закрыто

Эстимация ​

  • LOC: ~50-100 в ws/book.rs (audit + observable failures) + ~30-50 в scanner.rs (resync trigger) + 20-30 LOC метрик = ~150 LOC всего
  • Тесты: 4-6 unit (parser strict mode) + 1-2 integration (reconnect → resync flow)

Proposed status ​

План — fix будет после BUG-11 merge (или параллельно — не конфликтуют по файлам).