Build 060.24: implement Runtime Recovery Architecture
This commit is contained in:
552
docs/migrations/build_060_24.md
Normal file
552
docs/migrations/build_060_24.md
Normal file
@@ -0,0 +1,552 @@
|
||||
# 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.md` — итоговая архитектурная спецификация Build.
|
||||
- `build_060_23.md` — Trade Stream Acquisition Integration.
|
||||
- `build_060_23_architecture.md` — архитектура интеграции Trade Stream Acquisition.
|
||||
- `build_060_22.md` — Runtime Service Integration.
|
||||
- `build_060_20_1.md` — Trade Stream State Ownership Alignment.
|
||||
- `build_060_19.md` — Trade Recovery.
|
||||
- `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.25–060.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.25–060.26.
|
||||
|
||||
---
|
||||
|
||||
## 11. Итог
|
||||
|
||||
Build 060.24 завершает внутреннюю Runtime Recovery Architecture.
|
||||
|
||||
Проект располагает:
|
||||
|
||||
- надёжным checkpoint в Consistency Layer;
|
||||
- детерминированным планированием Recovery Window;
|
||||
- изолированными компонентами reconnect, heartbeat, supervisor
|
||||
и scheduler;
|
||||
- координатором последовательного восстановления;
|
||||
- явным composition root с единым состоянием для live и recovery.
|
||||
|
||||
Следующий этап не должен заново проектировать эти компоненты.
|
||||
Задача Build 060.25 — подключить готовый граф к production WebSocket
|
||||
lifecycle и определить end-to-end порядок запуска, reconnect и recovery.
|
||||
1010
docs/migrations/build_060_24_architecture.md
Normal file
1010
docs/migrations/build_060_24_architecture.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user