19 KiB
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-сделки:
WebSocket message
│
▼
TradeStreamMessageAdapterProtocol
│
▼
Canonical Trade
│
▼
TradeStreamConsistencyController
│
▼
Trade | None
Также уже существовал REST Recovery Layer:
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 хранит:
last_trade_id
last_trade
recent trade window
Checkpoint — это last_trade, то есть последняя каноническая сделка,
принятая Consistency Layer.
Он обновляется только после успешного accept(). Полный дубликат,
конфликтующий дубликат, нарушение порядка или неожиданный symbol
не переводят checkpoint вперёд.
Основной инвариант:
last_trade is None
⇔
last_trade_id is None
Если checkpoint существует:
last_trade.trade_id == last_trade_id
last_trade.symbol == TradeStreamState.symbol
Checkpoint находится только в оперативной памяти. Его долговременное хранение относится к Build 060.28.
4.2. TradeRecoveryWindow
TradeRecoveryWindow — immutable value object с slots:
symbol
start_time
end_time
Модель проверяет типы и запрещает:
- пустой symbol;
- отрицательные временные границы;
start_time > end_time;- использование
boolвместо целочисленного timestamp.
Window не выполняет Recovery и не содержит transport-, retry- или exchange-состояние.
4.3. TradeRecoveryWindowPlanner
Planner принимает полный диапазон и возвращает:
tuple[TradeRecoveryWindow, ...]
Каждое окно удовлетворяет ограничению существующего
TradeRecoveryRequest:
end_time - start_time < 3_600_000 ms
Значение по умолчанию:
3_599_999 ms
Соседние окна имеют общую границу:
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:
ReconnectStartedEvent
│
▼
ConnectCommand
│
▼
restore_subscriptions()
│
▼
ReconnectCompletedEvent
При ошибке публикуется ReconnectFailedEvent, состояние переходит
в FAILED, а исходное исключение распространяется вызывающему коду.
Coordinator хранит номер попытки и состояние:
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-сессии:
STOPPED
RUNNING
RECONNECTING
FAILED
Он:
- запускает и останавливает Heartbeat;
- принимает уведомления об активности;
- запускает одну reconnect-попытку после подтверждённого timeout;
- предотвращает параллельный reconnect;
- после успешного reconnect начинает новый heartbeat-период;
- сохраняет
FAILEDи распространяет ошибку reconnect.
Supervisor не выполняет периодический цикл, recovery, retry или backoff.
4.7. RuntimeScheduler
Scheduler отвечает только за периодичность:
HeartbeatMonitor.check_timeout()
│
├── False → следующий интервал
│
└── True → RuntimeSupervisor.handle_heartbeat_timeout()
Цикл запускается явным await RuntimeScheduler.start(). Scheduler
не создаёт asyncio.Task самостоятельно. stop() запрашивает безопасное
завершение на ближайшей управляемой границе.
4.8. RuntimeRecoveryCoordinator
Coordinator соединяет существующие слои:
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:
build_trade_stream_runtime_composition(...)
создаёт один граф зависимостей и возвращает immutable
TradeStreamRuntimeComposition.
Ключевой identity-инвариант:
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
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
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
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 |
Контрольный результат:
230 passed
Полная регрессия проекта:
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
- Checkpoint принадлежит
TradeStreamState. - Checkpoint обновляется только принятой сделкой.
- Реализована immutable-модель Recovery Window.
- Реализован расчёт одного или нескольких допустимых окон.
- Реализована одна reconnect-попытка и восстановление подписок.
- Реализован пассивный Heartbeat.
- Реализовано состояние и поведение Runtime Supervisor.
- Реализован периодический Runtime Scheduler.
- Реализован Runtime Recovery Coordinator.
- Реализована Runtime Composition с общим Consistency Layer.
- Создание Composition не имеет lifecycle- и network-side effects.
- Целевые тесты проходят.
- Полная регрессия проходит.
- 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.