Тема
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)
- В dev/test:
- Каждое решение «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— на eventWsReconnected:- Для каждого subscribed asset →
clob.fetch_orderbook_rest_with_metadata(asset) - Применить через
OrderbookStore::update_from_rest_snapshot(ADR-022 4-branch guard сам разрулит race с свежими WS deltas)
- Для каждого subscribed asset →
- Метрика:
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}— counterws_reconnect_total— counterws_resync_assets_total— counterws_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-OPENR-WS-RECONNECT-CASE-PING-TIMEOUTR-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 (или параллельно — не конфликтуют по файлам).