Files
dzentra_bot/docs/migrations/build_060_24.md

561 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Build 060.24 — Runtime Recovery Architecture
**Engineering Migration Report**
---
## Контроль документа
| Свойство | Значение |
|---|---|
| Build | 060.24 |
| Название | Runtime Recovery Architecture |
| Статус | Completed |
| Проект | Dzentra |
| Подсистема | Market Data Acquisition |
| Компонент | Trade Stream Runtime |
| Версия документа | 2.0 |
## Связанные документы
- [Build 060.24 Architecture](build_060_24_architecture.md) — итоговая
архитектурная спецификация Build.
- [Build 060.23 Engineering Migration Report](build_060_23.md) — Trade
Stream Acquisition Integration.
- [Build 060.23 Architecture](build_060_23_architecture.md) — архитектура
интеграции Trade Stream Acquisition.
- [Build 060.22 Engineering Migration Report](build_060_22.md) — Runtime
Service Integration.
- [Build 060.20.1 Engineering Migration Report](build_060_20_1.md) —
Trade Stream State Ownership Alignment.
- [Build 060.19 Engineering Migration Report](build_060_19.md) — Trade
Recovery.
- [Build 060.18 Engineering Migration Report](build_060_18.md) — Trade
Stream Consistency.
---
## 1. Исходное состояние
После Build 060.23 проект имел рабочий конвейер обработки одной live-сделки:
```text
WebSocket message
TradeStreamMessageAdapterProtocol
Canonical Trade
TradeStreamConsistencyController
Trade | None
```
Также уже существовал REST Recovery Layer:
```text
TradeRecoveryRequest
TradeRecoveryController
TradeRecoveryNormalizer
TradeStreamConsistencyController
```
Эти два конвейера использовали Consistency Layer, но Runtime ещё не
обладал архитектурой обнаружения потери активности, повторного подключения
и координации восстановления пропущенного диапазона.
## 2. Цель Build
Цель Build 060.24 — реализовать внутреннюю Runtime Recovery Architecture:
- определить единственный checkpoint Trade Stream;
- представить и рассчитать допустимые окна восстановления;
- реализовать изолированные runtime-компоненты reconnect, heartbeat,
supervisor и scheduler;
- связать checkpoint, planner и существующий Recovery Controller;
- построить единый граф зависимостей Live Stream и Recovery;
- не запускать production lifecycle и не создавать сетевые подключения
во время композиции.
Build создаёт завершённую архитектурную основу восстановления в памяти.
Её подключение к реальному WebSocket lifecycle, bootstrap приложения
и production-конфигурации относится к Build 060.25.
---
## 3. Выполненный roadmap
| Подэтап | Название | Фактическая реализация |
|---|---|---|
| 060.24.1 | RuntimeCheckpoint | `TradeStreamState.last_trade` |
| 060.24.2 | RuntimeSessionState | `RuntimeSupervisorState` и локальные состояния runtime-компонентов |
| 060.24.3 | RecoveryPlanner | `TradeRecoveryWindowPlanner` |
| 060.24.4 | RecoveryWindowCalculator | алгоритм `TradeRecoveryWindowPlanner.build_windows()` |
| 060.24.5 | ReconnectCoordinator | `ReconnectCoordinator` |
| 060.24.6 | Heartbeat | `HeartbeatMonitor` |
| 060.24.7 | Supervisor | `RuntimeSupervisor` |
| 060.24.8 | Scheduler | `RuntimeScheduler` |
| 060.24.9.1 | RuntimeRecoveryCoordinator | `RuntimeRecoveryProtocol` и `RuntimeRecoveryCoordinator` |
| 060.24.9.2 | Runtime Composition | `TradeStreamRuntimeComposition` и factory-функция сборки |
`RuntimeSessionState` не введён как отдельная дублирующая модель.
Состояние runtime-сессии выражено через `RuntimeSupervisorState`, а
локальные детали принадлежат `ReconnectState` и `HeartbeatState`.
`RecoveryWindowCalculator` не введён как отдельный класс. Расчёт является
единственной ответственностью `TradeRecoveryWindowPlanner`, поэтому его
выделение создало бы искусственный слой без самостоятельного состояния
или политики.
---
## 4. Реализованные компоненты
### 4.1. RuntimeCheckpoint
`TradeStreamState` хранит:
```text
last_trade_id
last_trade
recent trade window
```
Checkpoint — это `last_trade`, то есть последняя каноническая сделка,
принятая Consistency Layer.
Он обновляется только после успешного `accept()`. Полный дубликат,
конфликтующий дубликат, нарушение порядка или неожиданный symbol
не переводят checkpoint вперёд.
Основной инвариант:
```text
last_trade is None
last_trade_id is None
```
Если checkpoint существует:
```text
last_trade.trade_id == last_trade_id
last_trade.symbol == TradeStreamState.symbol
```
Checkpoint находится только в оперативной памяти. Его долговременное
хранение относится к Build 060.28.
### 4.2. TradeRecoveryWindow
`TradeRecoveryWindow` — immutable value object с `slots`:
```text
symbol
start_time
end_time
```
Модель проверяет типы и запрещает:
- пустой symbol;
- отрицательные временные границы;
- `start_time > end_time`;
- использование `bool` вместо целочисленного timestamp.
Window не выполняет Recovery и не содержит transport-, retry- или
exchange-состояние.
### 4.3. TradeRecoveryWindowPlanner
Planner принимает полный диапазон и возвращает:
```text
tuple[TradeRecoveryWindow, ...]
```
Каждое окно удовлетворяет ограничению существующего
`TradeRecoveryRequest`:
```text
end_time - start_time < 3_600_000 ms
```
Значение по умолчанию:
```text
3_599_999 ms
```
Соседние окна имеют общую границу:
```text
previous.end_time == next.start_time
```
Возможный повтор сделки на границе устраняется общим Consistency Layer.
При равных `start_time` и `end_time` Planner возвращает пустой tuple.
Planner:
- не выполняет REST-запросы;
- не создаёт `TradeRecoveryRequest`;
- не читает системное время;
- не зависит от WebSocket и Runtime lifecycle.
### 4.4. ReconnectCoordinator
`ReconnectCoordinator` выполняет одну попытку reconnect:
```text
ReconnectStartedEvent
ConnectCommand
restore_subscriptions()
ReconnectCompletedEvent
```
При ошибке публикуется `ReconnectFailedEvent`, состояние переходит
в `FAILED`, а исходное исключение распространяется вызывающему коду.
Coordinator хранит номер попытки и состояние:
```text
DISCONNECTED
CONNECTING
RESTORING_SUBSCRIPTIONS
CONNECTED
FAILED
```
Retry loop и backoff в этот компонент не входят.
### 4.5. HeartbeatMonitor
Heartbeat является пассивным монитором:
- использует monotonic clock;
- хранит время последней активности;
- определяет timeout детерминированным вызовом `check_timeout()`;
- публикует один `HeartbeatTimeoutEvent` на один период отсутствия
активности;
- возвращается в monitoring после новой активности.
Heartbeat не создаёт фоновых задач, не выполняет reconnect и не управляет
WebSocket.
### 4.6. RuntimeSupervisor
Supervisor владеет состоянием runtime-сессии:
```text
STOPPED
RUNNING
RECONNECTING
FAILED
```
Он:
- запускает и останавливает Heartbeat;
- принимает уведомления об активности;
- запускает одну reconnect-попытку после подтверждённого timeout;
- предотвращает параллельный reconnect;
- после успешного reconnect начинает новый heartbeat-период;
- сохраняет `FAILED` и распространяет ошибку reconnect.
Supervisor не выполняет периодический цикл, recovery, retry или backoff.
### 4.7. RuntimeScheduler
Scheduler отвечает только за периодичность:
```text
HeartbeatMonitor.check_timeout()
├── False → следующий интервал
└── True → RuntimeSupervisor.handle_heartbeat_timeout()
```
Цикл запускается явным `await RuntimeScheduler.start()`. Scheduler
не создаёт `asyncio.Task` самостоятельно. `stop()` запрашивает безопасное
завершение на ближайшей управляемой границе.
### 4.8. RuntimeRecoveryCoordinator
Coordinator соединяет существующие слои:
```text
TradeStreamStateStore
TradeStreamState.last_trade
executed_at → Unix milliseconds
TradeRecoveryWindowPlanner
TradeRecoveryRequest[]
TradeRecoveryController
aggregated TradeRecoveryResult
```
Свойства реализации:
- использует существующее состояние и не создаёт новый
`TradeStreamState`;
- принимает `recovery_end_time` от вызывающего runtime-компонента;
- преобразует только timezone-aware `datetime`;
- не использует float при переводе времени в Unix milliseconds;
- выполняет окна последовательно;
- объединяет восстановленные сделки в один результат;
- не скрывает ошибки Store, Planner или Recovery Controller.
При отсутствии состояния или checkpoint возвращается пустой
`TradeRecoveryResult`; обе его границы равны `recovery_end_time`.
### 4.9. Runtime Composition
Factory:
```text
build_trade_stream_runtime_composition(...)
```
создаёт один граф зависимостей и возвращает immutable
`TradeStreamRuntimeComposition`.
Ключевой identity-инвариант:
```text
Live Stream ───┐
├── one TradeStreamConsistencyController
Recovery ──────┘
one TradeStreamStateStore
```
Благодаря этому live-сделки и восстановленные сделки проходят одинаковые
правила согласованности и используют один checkpoint.
Внешние WebSocket-зависимости передаются через Protocol-контракты.
Создание Composition:
- не читает Settings;
- не создаёт production transport;
- не подключается к сети;
- не запускает Scheduler или Supervisor;
- не создаёт фоновые задачи;
- не выполняет Recovery.
---
## 5. Архитектурные границы
После Build действуют следующие границы.
| Компонент | Владеет | Не владеет |
|---|---|---|
| `TradeStreamState` | checkpoint и deduplication window | recovery orchestration |
| `TradeRecoveryWindowPlanner` | расчёт окон | REST и runtime lifecycle |
| `ReconnectCoordinator` | одна reconnect-попытка | retry, backoff и recovery |
| `HeartbeatMonitor` | контроль активности | scheduling и reconnect |
| `RuntimeSupervisor` | состояние сессии и реакция на timeout | периодический цикл и recovery |
| `RuntimeScheduler` | периодический вызов проверок | reconnect-логика и heartbeat-вычисления |
| `RuntimeRecoveryCoordinator` | recovery orchestration одного symbol | WebSocket lifecycle |
| Composition root | создание и связывание объектов | запуск production lifecycle |
Ни один из компонентов Build не импортирует bootstrap приложения,
торговую логику или Telegram-инфраструктуру.
---
## 6. Состав изменений
### Production
```text
app/src/market_data/acquisition/
├── consistency/
│ └── trade_stream_state.py
├── recovery/
│ ├── trade_recovery_window.py
│ └── trade_recovery_window_planner.py
├── runtime/
│ ├── heartbeat.py
│ ├── reconnect.py
│ ├── runtime_recovery_coordinator.py
│ ├── runtime_recovery_protocol.py
│ ├── scheduler.py
│ └── supervisor.py
└── trade_stream_runtime_composition.py
```
### Tests
```text
app/tests/unit/market_data/acquisition/
├── consistency/
│ └── test_trade_stream_state.py
├── recovery/
│ ├── test_trade_recovery_window.py
│ └── test_trade_recovery_window_planner.py
├── runtime/
│ ├── test_heartbeat_monitor.py
│ ├── test_reconnect_coordinator.py
│ ├── test_runtime_recovery_coordinator.py
│ ├── test_runtime_scheduler.py
│ └── test_runtime_supervisor.py
└── test_trade_stream_runtime_composition.py
```
### Documentation
```text
docs/migrations/
├── build_060_24.md
└── build_060_24_architecture.md
```
Изменение `.gitignore`, присутствующее в рабочем дереве, не является
частью Build 060.24.
---
## 7. Тестирование
Непосредственно связанные с Build девять тестовых модулей содержат
230 тестов.
| Область | Тестов |
|---|---:|
| Trade Stream State | 24 |
| Recovery Window | 20 |
| Recovery Window Planner | 36 |
| Reconnect Coordinator | 15 |
| Heartbeat Monitor | 28 |
| Runtime Supervisor | 25 |
| Runtime Scheduler | 30 |
| Runtime Recovery Coordinator | 27 |
| Runtime Composition | 25 |
| **Итого** | **230** |
Контрольный результат:
```text
230 passed
```
Полная регрессия проекта:
```text
1620 passed
0 failed
```
Тесты являются unit- и composition-тестами. Реальные сценарии
WebSocket disconnect, повторной подписки, reconnect → recovery
и стресс-тестирование относятся к Build 060.25060.26.
---
## 8. Обратная совместимость
Build сохраняет существующие публичные контракты:
- каноническая модель `Trade` не изменена;
- Recovery Controller и Recovery Request не изменены;
- live acquisition продолжает использовать Consistency Protocol;
- transport protocols остаются внешними зависимостями Runtime;
- `main.py` и bootstrap не изменены.
Расширение `TradeStreamState` добавляет checkpoint, не изменяя контракт
`accept()`.
---
## 9. Что не входит в Build
Build 060.24 не включает:
- создание production WebSocket transport/session;
- подключение Composition к `main.py` или bootstrap;
- запуск scheduler-задачи;
- передачу live-активности в Supervisor;
- выбор symbol и `recovery_end_time` для production recovery;
- автоматическую цепочку reconnect → recovery;
- переключение между Recovery и Live Stream;
- retry/backoff policy;
- постоянное хранение checkpoint;
- интеграционные reconnect/recovery и stress tests.
Эти ограничения означают, что формулировка
«Trade Stream самостоятельно переживает обрыв WebSocket» на данном этапе
является архитектурной целью, а не активированным production-поведением.
Production Runtime Integration выполняется в Build 060.25.
---
## 10. Definition of Done
- [x] Checkpoint принадлежит `TradeStreamState`.
- [x] Checkpoint обновляется только принятой сделкой.
- [x] Реализована immutable-модель Recovery Window.
- [x] Реализован расчёт одного или нескольких допустимых окон.
- [x] Реализована одна reconnect-попытка и восстановление подписок.
- [x] Реализован пассивный Heartbeat.
- [x] Реализовано состояние и поведение Runtime Supervisor.
- [x] Реализован периодический Runtime Scheduler.
- [x] Реализован Runtime Recovery Coordinator.
- [x] Реализована Runtime Composition с общим Consistency Layer.
- [x] Создание Composition не имеет lifecycle- и network-side effects.
- [x] Целевые тесты проходят.
- [x] Полная регрессия проходит.
- [ ] Production lifecycle подключён к приложению — Build 060.25.
- [ ] End-to-end reconnect/recovery проверен — Build 060.25060.26.
---
## 11. Итог
Build 060.24 завершает внутреннюю Runtime Recovery Architecture.
Проект располагает:
- надёжным checkpoint в Consistency Layer;
- детерминированным планированием Recovery Window;
- изолированными компонентами reconnect, heartbeat, supervisor
и scheduler;
- координатором последовательного восстановления;
- явным composition root с единым состоянием для live и recovery.
Следующий этап не должен заново проектировать эти компоненты.
Задача [Build 060.25](build_060_25.md) — подключить готовый граф к
production WebSocket lifecycle и определить end-to-end порядок запуска,
reconnect и recovery.