1011 lines
27 KiB
Markdown
1011 lines
27 KiB
Markdown
# Build 060.24 — Runtime Recovery Architecture Specification
|
||
|
||
**Статус:** Accepted
|
||
|
||
**Build:** 060.24
|
||
|
||
**Подсистема:** Market Data Acquisition / Trade Stream Runtime
|
||
|
||
---
|
||
|
||
## 1. Назначение
|
||
|
||
Документ фиксирует фактическую архитектуру Build 060.24 после завершения
|
||
подэтапов 060.24.1–060.24.9.2.
|
||
|
||
Build создаёт компоненты, необходимые для восстановления Trade Stream
|
||
после потери активности, и связывает их в единый граф зависимостей.
|
||
|
||
Документ отделяет:
|
||
|
||
- завершённую внутреннюю архитектуру Build 060.24;
|
||
- production lifecycle и end-to-end интеграцию Build 060.25;
|
||
- интеграционные и стресс-сценарии Build 060.26;
|
||
- постоянное хранение Build 060.27–060.28.
|
||
|
||
---
|
||
|
||
## 2. Архитектурный контекст
|
||
|
||
### 2.1. Live pipeline
|
||
|
||
```text
|
||
WebSocket Transport
|
||
│
|
||
▼
|
||
AcquisitionRuntimeService
|
||
│
|
||
▼
|
||
TradeStreamMessageAdapterProtocol
|
||
│
|
||
▼
|
||
TradeStreamAcquisitionService
|
||
│
|
||
▼
|
||
TradeStreamConsistencyController
|
||
│
|
||
▼
|
||
Canonical Trade | None
|
||
```
|
||
|
||
### 2.2. Recovery pipeline
|
||
|
||
```text
|
||
TradeRecoveryRequest
|
||
│
|
||
▼
|
||
TradeRecoveryController
|
||
│
|
||
▼
|
||
DzengiTradesDocumentSource
|
||
│
|
||
▼
|
||
TradeRecoveryNormalizer
|
||
│
|
||
▼
|
||
TradeStreamConsistencyController
|
||
│
|
||
▼
|
||
Canonical Trade[]
|
||
```
|
||
|
||
### 2.3. Проблема до Build
|
||
|
||
До Build компоненты Live и Recovery существовали отдельно.
|
||
Отсутствовали:
|
||
|
||
- надёжная временная точка начала восстановления;
|
||
- разбиение диапазона на допустимые REST-окна;
|
||
- контроль потери runtime-активности;
|
||
- одна формализованная reconnect-попытка;
|
||
- состояние runtime-сессии;
|
||
- периодический вызов heartbeat;
|
||
- координатор checkpoint → recovery;
|
||
- composition root с общими stateful-зависимостями.
|
||
|
||
---
|
||
|
||
## 3. Итоговая модель
|
||
|
||
```text
|
||
PRODUCTION INPUTS
|
||
|
||
WebSocket Session / Transport / Subscriptions / Event Publisher
|
||
│
|
||
▼
|
||
TradeStreamRuntimeComposition
|
||
│
|
||
┌────────────────────┼────────────────────┐
|
||
│ │ │
|
||
▼ ▼ ▼
|
||
LIVE ACQUISITION RUNTIME LIFECYCLE RECOVERY
|
||
│ │ │
|
||
│ HeartbeatMonitor │
|
||
│ │ │
|
||
│ RuntimeScheduler │
|
||
│ │ │
|
||
│ RuntimeSupervisor │
|
||
│ │ │
|
||
│ ReconnectCoordinator │
|
||
│ │
|
||
▼ ▼
|
||
TradeStreamConsistencyController ◄──── TradeRecoveryController
|
||
│ ▲
|
||
▼ │
|
||
TradeStreamStateStore ──► RuntimeRecoveryCoordinator
|
||
│ │
|
||
▼ ▼
|
||
TradeStreamState TradeRecoveryWindowPlanner
|
||
│ │
|
||
└──── checkpoint ─────────┘
|
||
```
|
||
|
||
Composition создаёт граф, но не запускает его. Владельцем production
|
||
задач и lifecycle будет внешний bootstrap Build 060.25.
|
||
|
||
---
|
||
|
||
## 4. Слои и направления зависимостей
|
||
|
||
Допустимое направление:
|
||
|
||
```text
|
||
Composition
|
||
│
|
||
├── Runtime
|
||
├── Recovery
|
||
├── Consistency
|
||
└── Acquisition
|
||
|
||
Runtime Recovery Coordinator
|
||
│
|
||
├── State Store Protocol
|
||
├── Window Planner
|
||
└── Recovery Protocol
|
||
|
||
Recovery Controller
|
||
│
|
||
└── Consistency Protocol
|
||
|
||
Acquisition Service
|
||
│
|
||
└── Consistency Protocol
|
||
```
|
||
|
||
Запрещённые обратные зависимости:
|
||
|
||
```text
|
||
Consistency ─X─► Runtime
|
||
Recovery ─X─► WebSocket lifecycle
|
||
Heartbeat ─X─► Reconnect implementation
|
||
Scheduler ─X─► Transport
|
||
Supervisor ─X─► Recovery Controller
|
||
```
|
||
|
||
Composition размещена в корне `market_data.acquisition`, а не внутри
|
||
`runtime`, потому что она знает сразу о нескольких соседних слоях.
|
||
|
||
---
|
||
|
||
## 5. Ownership состояния
|
||
|
||
### 5.1. Trade Stream state
|
||
|
||
Владелец:
|
||
|
||
```text
|
||
TradeStreamStateStore
|
||
│
|
||
▼
|
||
TradeStreamState(symbol)
|
||
```
|
||
|
||
Содержит:
|
||
|
||
- последнюю принятую сделку;
|
||
- идентификатор последней принятой сделки;
|
||
- ограниченное окно deduplication.
|
||
|
||
### 5.2. Runtime session state
|
||
|
||
Отдельный `RuntimeSessionState` не создаётся.
|
||
|
||
Общее состояние lifecycle принадлежит Supervisor:
|
||
|
||
```text
|
||
RuntimeSupervisorState
|
||
├── STOPPED
|
||
├── RUNNING
|
||
├── RECONNECTING
|
||
└── FAILED
|
||
```
|
||
|
||
Локальное состояние операции reconnect принадлежит
|
||
`ReconnectCoordinator`:
|
||
|
||
```text
|
||
ReconnectState
|
||
├── DISCONNECTED
|
||
├── CONNECTING
|
||
├── RESTORING_SUBSCRIPTIONS
|
||
├── CONNECTED
|
||
└── FAILED
|
||
```
|
||
|
||
Состояние контроля активности принадлежит Heartbeat:
|
||
|
||
```text
|
||
HeartbeatState
|
||
├── IDLE
|
||
├── MONITORING
|
||
└── TIMED_OUT
|
||
```
|
||
|
||
Это разделение предотвращает появление общего mutable-объекта,
|
||
дублирующего состояние специализированных компонентов.
|
||
|
||
### 5.3. Scheduler state
|
||
|
||
Scheduler хранит только:
|
||
|
||
```text
|
||
running: bool
|
||
```
|
||
|
||
Флаг описывает состояние его цикла и не является состоянием
|
||
WebSocket-сессии.
|
||
|
||
---
|
||
|
||
## 6. RuntimeCheckpoint
|
||
|
||
### 6.1. Владелец
|
||
|
||
Checkpoint принадлежит только:
|
||
|
||
```text
|
||
TradeStreamState.last_trade
|
||
```
|
||
|
||
Runtime, Recovery Coordinator и Supervisor могут читать checkpoint,
|
||
но не владеют им и не изменяют его.
|
||
|
||
### 6.2. Причина хранения полного Trade
|
||
|
||
Полный канонический `Trade` уже содержит согласованные:
|
||
|
||
```text
|
||
symbol
|
||
trade_id
|
||
executed_at
|
||
price
|
||
quantity
|
||
side
|
||
source
|
||
```
|
||
|
||
Отдельные поля checkpoint создавали бы риск рассинхронизации.
|
||
|
||
### 6.3. Инварианты
|
||
|
||
```text
|
||
last_trade is None
|
||
⇔
|
||
last_trade_id is None
|
||
```
|
||
|
||
```text
|
||
last_trade.trade_id == last_trade_id
|
||
```
|
||
|
||
```text
|
||
last_trade.symbol == state.symbol
|
||
```
|
||
|
||
Checkpoint изменяется только после принятия сделки. Ошибка или duplicate
|
||
не должны частично изменить состояние.
|
||
|
||
### 6.4. Ограничение
|
||
|
||
Checkpoint является in-memory. Перезапуск процесса его уничтожает.
|
||
Persistent Checkpoint относится к Build 060.28.
|
||
|
||
---
|
||
|
||
## 7. Recovery Window и расчёт диапазона
|
||
|
||
### 7.1. TradeRecoveryWindow
|
||
|
||
Value object:
|
||
|
||
```text
|
||
TradeRecoveryWindow(
|
||
symbol: str,
|
||
start_time: int,
|
||
end_time: int,
|
||
)
|
||
```
|
||
|
||
Свойства:
|
||
|
||
- immutable;
|
||
- slots;
|
||
- валиден сразу после создания;
|
||
- независим от REST, Runtime и конкретной биржи.
|
||
|
||
### 7.2. TradeRecoveryWindowPlanner
|
||
|
||
Planner объединяет роли RecoveryPlanner и RecoveryWindowCalculator:
|
||
|
||
```text
|
||
build_windows(
|
||
symbol,
|
||
start_time,
|
||
end_time,
|
||
) -> tuple[TradeRecoveryWindow, ...]
|
||
```
|
||
|
||
Отдельный Calculator не вводится, потому что расчёт окон:
|
||
|
||
- не имеет собственного состояния;
|
||
- не имеет политики за пределами Planner;
|
||
- не используется независимо от построения recovery plan.
|
||
|
||
### 7.3. Временные границы
|
||
|
||
`start_time` и `end_time` представлены Unix milliseconds.
|
||
|
||
Допустимый диапазон:
|
||
|
||
```text
|
||
0 <= start_time <= end_time
|
||
```
|
||
|
||
Равные границы означают отсутствие необходимого восстановления:
|
||
|
||
```text
|
||
start_time == end_time
|
||
│
|
||
▼
|
||
()
|
||
```
|
||
|
||
### 7.4. Размер окна
|
||
|
||
Ограничение Recovery Request:
|
||
|
||
```text
|
||
end_time - start_time < 3_600_000
|
||
```
|
||
|
||
Максимальное значение Planner:
|
||
|
||
```text
|
||
3_599_999 ms
|
||
```
|
||
|
||
### 7.5. Непрерывность
|
||
|
||
Planner строит последовательность:
|
||
|
||
```text
|
||
[start, boundary_1]
|
||
[boundary_1, boundary_2]
|
||
[boundary_2, end]
|
||
```
|
||
|
||
Соседние окна разделяют одну границу. Это исключает временной разрыв.
|
||
Повтор сделки на общей границе безопасен, потому что Recovery использует
|
||
тот же Consistency Layer.
|
||
|
||
---
|
||
|
||
## 8. Reconnect
|
||
|
||
### 8.1. Ответственность
|
||
|
||
`ReconnectCoordinator.reconnect()` выполняет ровно одну попытку:
|
||
|
||
```text
|
||
attempt += 1
|
||
│
|
||
▼
|
||
publish ReconnectStartedEvent
|
||
│
|
||
▼
|
||
dispatch ConnectCommand
|
||
│
|
||
▼
|
||
restore_subscriptions()
|
||
│
|
||
▼
|
||
publish ReconnectCompletedEvent
|
||
```
|
||
|
||
### 8.2. Ошибка
|
||
|
||
```text
|
||
connect or restore error
|
||
│
|
||
▼
|
||
state = FAILED
|
||
│
|
||
▼
|
||
publish ReconnectFailedEvent
|
||
│
|
||
▼
|
||
raise original exception
|
||
```
|
||
|
||
### 8.3. Граница
|
||
|
||
Reconnect не решает:
|
||
|
||
- когда запускаться;
|
||
- сколько раз повторяться;
|
||
- какой backoff использовать;
|
||
- требуется ли Trade Recovery;
|
||
- когда возобновлять live processing.
|
||
|
||
---
|
||
|
||
## 9. Heartbeat
|
||
|
||
Heartbeat — пассивный детектор отсутствия активности.
|
||
|
||
```text
|
||
start()
|
||
│
|
||
▼
|
||
MONITORING
|
||
│
|
||
├── record_activity() → reset monotonic boundary
|
||
│
|
||
└── check_timeout()
|
||
│
|
||
├── elapsed < timeout → False
|
||
└── elapsed >= timeout
|
||
│
|
||
▼
|
||
TIMED_OUT
|
||
│
|
||
▼
|
||
HeartbeatTimeoutEvent
|
||
```
|
||
|
||
Событие публикуется один раз до новой активности или нового `start()`.
|
||
|
||
Используется `time.monotonic`, а не wall clock. Инъекция clock делает
|
||
проверки детерминированными.
|
||
|
||
Heartbeat:
|
||
|
||
- не содержит собственного периодического цикла;
|
||
- не знает о Supervisor;
|
||
- не выполняет reconnect;
|
||
- не создаёт задачи.
|
||
|
||
---
|
||
|
||
## 10. Runtime Supervisor
|
||
|
||
Supervisor координирует Heartbeat и Reconnect.
|
||
|
||
### 10.1. Запуск
|
||
|
||
```text
|
||
STOPPED
|
||
│ start()
|
||
▼
|
||
heartbeat.start()
|
||
│
|
||
▼
|
||
RUNNING
|
||
```
|
||
|
||
### 10.2. Активность
|
||
|
||
`notify_activity()` передаётся Heartbeat только в состоянии `RUNNING`.
|
||
Активность после остановки или во время reconnect не запускает monitor
|
||
неявно.
|
||
|
||
### 10.3. Timeout
|
||
|
||
```text
|
||
RUNNING
|
||
│ confirmed timeout
|
||
▼
|
||
heartbeat.stop()
|
||
│
|
||
▼
|
||
RECONNECTING
|
||
│
|
||
▼
|
||
ReconnectCoordinator.reconnect()
|
||
│
|
||
├── success → heartbeat.start() → RUNNING
|
||
└── failure → FAILED → raise
|
||
```
|
||
|
||
Проверка исходного состояния предотвращает параллельный reconnect.
|
||
|
||
### 10.4. Граница
|
||
|
||
Supervisor не:
|
||
|
||
- вызывает `HeartbeatMonitor.check_timeout()`;
|
||
- выполняет периодический sleep;
|
||
- планирует retry;
|
||
- выполняет recovery;
|
||
- управляет Transport напрямую.
|
||
|
||
---
|
||
|
||
## 11. Runtime Scheduler
|
||
|
||
Scheduler владеет только временем вызовов:
|
||
|
||
```text
|
||
while running:
|
||
timed_out = await heartbeat.check_timeout()
|
||
|
||
if timed_out:
|
||
await supervisor.handle_heartbeat_timeout()
|
||
|
||
await sleep(interval_seconds)
|
||
```
|
||
|
||
Реальная реализация дополнительно:
|
||
|
||
- не запускает второй цикл при повторном `start()`;
|
||
- позволяет выполнить один шаг через `run_once()`;
|
||
- прекращает цикл через идемпотентный `stop()`;
|
||
- сбрасывает `running` в `finally`;
|
||
- распространяет ошибки зависимостей без обёртки;
|
||
- позволяет инъецировать sleep-функцию.
|
||
|
||
Scheduler не создаёт свою `asyncio.Task`. Внешний lifecycle определяет,
|
||
где и как await/cancel выполняются.
|
||
|
||
---
|
||
|
||
## 12. Runtime Recovery Coordinator
|
||
|
||
### 12.1. Контракт
|
||
|
||
```text
|
||
recover(
|
||
symbol: str,
|
||
recovery_end_time: int,
|
||
) -> TradeRecoveryResult
|
||
```
|
||
|
||
`RuntimeRecoveryProtocol` является structural protocol. Реализация
|
||
не наследует Protocol напрямую, что сохраняет рабочий `__slots__`
|
||
и позволяет проверять совместимость через `runtime_checkable`.
|
||
|
||
### 12.2. Последовательность
|
||
|
||
```text
|
||
state_store.get(symbol)
|
||
│
|
||
├── state absent ───────────┐
|
||
│ │
|
||
▼ │
|
||
state.last_trade │
|
||
│ │
|
||
├── checkpoint absent ──────┤
|
||
│ │
|
||
▼ ▼
|
||
executed_at → Unix ms empty TradeRecoveryResult
|
||
│
|
||
▼
|
||
window_planner.build_windows()
|
||
│
|
||
▼
|
||
for each window:
|
||
TradeRecoveryRequest
|
||
│
|
||
▼
|
||
recovery_controller.recover()
|
||
│
|
||
▼
|
||
aggregate recovered_trades
|
||
│
|
||
▼
|
||
TradeRecoveryResult
|
||
```
|
||
|
||
### 12.3. Time policy
|
||
|
||
Начальная граница берётся только из checkpoint.
|
||
|
||
Конечная граница передаётся извне:
|
||
|
||
```text
|
||
recovery_end_time
|
||
```
|
||
|
||
Coordinator не читает часы самостоятельно. Это оставляет production
|
||
policy владельцу lifecycle и обеспечивает детерминированные тесты.
|
||
|
||
`executed_at` обязан быть timezone-aware. Преобразование в Unix
|
||
milliseconds выполняется через UTC epoch и целочисленную арифметику.
|
||
|
||
### 12.4. Empty recovery
|
||
|
||
Если state или checkpoint отсутствуют:
|
||
|
||
```text
|
||
requested_start_time = recovery_end_time
|
||
requested_end_time = recovery_end_time
|
||
recovered_trades = ()
|
||
```
|
||
|
||
Coordinator не создаёт state автоматически, потому что владельцем
|
||
жизненного цикла state остаётся Consistency Layer.
|
||
|
||
### 12.5. Error policy
|
||
|
||
Ошибки Store, Planner и Recovery Controller распространяются без
|
||
преобразования. Runtime lifecycle должен решить, как публиковать ошибку
|
||
и какую retry policy применять.
|
||
|
||
---
|
||
|
||
## 13. Runtime Composition
|
||
|
||
### 13.1. Входные зависимости
|
||
|
||
Factory принимает:
|
||
|
||
- WebSocket session;
|
||
- transport;
|
||
- subscription manager;
|
||
- runtime event publisher;
|
||
- message adapter;
|
||
- recovery document source;
|
||
- heartbeat timeout;
|
||
- scheduler interval;
|
||
- необязательные window size, clock и sleep.
|
||
|
||
### 13.2. Создаваемые объекты
|
||
|
||
```text
|
||
TradeStreamStateStore
|
||
TradeStreamConsistencyController
|
||
TradeRecoveryController
|
||
TradeRecoveryWindowPlanner
|
||
RuntimeRecoveryCoordinator
|
||
AcquisitionRuntimeService
|
||
TradeStreamAcquisitionService
|
||
ReconnectCoordinator
|
||
HeartbeatMonitor
|
||
RuntimeSupervisor
|
||
RuntimeScheduler
|
||
```
|
||
|
||
### 13.3. Identity graph
|
||
|
||
```text
|
||
TradeStreamAcquisitionService
|
||
│
|
||
└──────────────┐
|
||
▼
|
||
TradeStreamConsistencyController
|
||
▲
|
||
│
|
||
TradeRecoveryController
|
||
│
|
||
▼
|
||
RuntimeRecoveryCoordinator
|
||
|
||
TradeStreamConsistencyController
|
||
│
|
||
▼
|
||
TradeStreamStateStore
|
||
▲
|
||
│
|
||
RuntimeRecoveryCoordinator
|
||
```
|
||
|
||
Один store и один consistency controller являются обязательным
|
||
инвариантом композиции.
|
||
|
||
### 13.4. Side-effect policy
|
||
|
||
Factory только создаёт объекты.
|
||
|
||
Она не:
|
||
|
||
- выполняет connect;
|
||
- создаёт asyncio-задачу;
|
||
- запускает Scheduler;
|
||
- запускает Supervisor;
|
||
- читает Settings;
|
||
- выполняет Recovery;
|
||
- публикует Runtime Events.
|
||
|
||
Это позволяет безопасно строить граф в тестах и в будущем bootstrap.
|
||
|
||
### 13.5. Не Service Locator
|
||
|
||
`TradeStreamRuntimeComposition` — typed assembly result, а не глобальный
|
||
registry. Он immutable и явно передаётся владельцу lifecycle.
|
||
|
||
---
|
||
|
||
## 14. Протоколы
|
||
|
||
На границах используются Protocol-контракты:
|
||
|
||
```text
|
||
WebSocketSessionProtocol
|
||
WebSocketTransportProtocol
|
||
WebSocketSubscriptionManagerProtocol
|
||
AcquisitionRuntimeCommandDispatcherProtocol
|
||
AcquisitionRuntimeEventPublisherProtocol
|
||
HeartbeatMonitorProtocol
|
||
ReconnectCoordinatorProtocol
|
||
RuntimeSupervisorProtocol
|
||
RuntimeSchedulerProtocol
|
||
RuntimeRecoveryProtocol
|
||
TradeStreamStateStoreProtocol
|
||
TradeRecoveryProtocol
|
||
```
|
||
|
||
Протоколы позволяют:
|
||
|
||
- независимо тестировать компоненты;
|
||
- не привязывать runtime-логику к конкретному transport;
|
||
- инъецировать clock и sleep;
|
||
- сохранить однонаправленные зависимости.
|
||
|
||
---
|
||
|
||
## 15. Ошибки и восстановление состояния
|
||
|
||
| Компонент | Поведение при ошибке |
|
||
|---|---|
|
||
| Checkpoint | не изменяется при отклонённой сделке |
|
||
| Planner | отклоняет некорректный диапазон до построения окон |
|
||
| Reconnect | публикует failure event, устанавливает `FAILED`, распространяет ошибку |
|
||
| Heartbeat | распространяет ошибку publisher |
|
||
| Supervisor | устанавливает `FAILED`, распространяет reconnect error |
|
||
| Scheduler | завершает цикл, сбрасывает `running`, распространяет ошибку |
|
||
| Recovery Coordinator | не скрывает ошибки Store, Planner и Recovery |
|
||
| Composition | ошибка конструктора прерывает сборку без запуска lifecycle |
|
||
|
||
Build не вводит общий error envelope и retry policy.
|
||
|
||
---
|
||
|
||
## 16. Concurrency и lifecycle
|
||
|
||
Компоненты Build не создают неявных фоновых задач.
|
||
|
||
Единственный длительный цикл находится в:
|
||
|
||
```text
|
||
await RuntimeScheduler.start()
|
||
```
|
||
|
||
Владелец внешнего lifecycle обязан:
|
||
|
||
- создать и хранить task Scheduler;
|
||
- вызвать `RuntimeSupervisor.start()`;
|
||
- передавать live activity через `notify_activity()`;
|
||
- остановить Scheduler и Supervisor;
|
||
- обработать cancellation;
|
||
- определить reconnect/recovery order.
|
||
|
||
Эти обязанности относятся к Production Runtime Integration.
|
||
|
||
---
|
||
|
||
## 17. Архитектурные решения
|
||
|
||
### ADR-060.24-001 — Checkpoint принадлежит Consistency Layer
|
||
|
||
**Статус:** Accepted
|
||
|
||
`TradeStreamState.last_trade` является единственным источником точки
|
||
восстановления.
|
||
|
||
### ADR-060.24-002 — Checkpoint хранит полный Canonical Trade
|
||
|
||
**Статус:** Accepted
|
||
|
||
Отдельные timestamp, symbol и trade id не дублируются в Runtime.
|
||
|
||
### ADR-060.24-003 — RuntimeSessionState распределён по владельцам
|
||
|
||
**Статус:** Accepted
|
||
|
||
Общий lifecycle хранится Supervisor, локальные состояния — Heartbeat
|
||
и Reconnect. Общий mutable session object не создаётся.
|
||
|
||
### ADR-060.24-004 — Window Calculator является частью Planner
|
||
|
||
**Статус:** Accepted
|
||
|
||
Расчёт окна не имеет независимой ответственности, поэтому отдельный
|
||
Calculator не вводится.
|
||
|
||
### ADR-060.24-005 — Shared boundary устраняется Consistency Layer
|
||
|
||
**Статус:** Accepted
|
||
|
||
Окна имеют общую временную границу, а возможные повторы удаляются
|
||
централизованной deduplication-логикой.
|
||
|
||
### ADR-060.24-006 — Heartbeat пассивен
|
||
|
||
**Статус:** Accepted
|
||
|
||
Heartbeat не владеет task или scheduler. Он только хранит активность
|
||
и проверяет timeout.
|
||
|
||
### ADR-060.24-007 — Reconnect выполняет одну попытку
|
||
|
||
**Статус:** Accepted
|
||
|
||
Retry и backoff остаются внешней policy.
|
||
|
||
### ADR-060.24-008 — Recovery End Time передаётся извне
|
||
|
||
**Статус:** Accepted
|
||
|
||
Coordinator не читает системное время и не принимает production policy.
|
||
|
||
### ADR-060.24-009 — Runtime Recovery не владеет WebSocket
|
||
|
||
**Статус:** Accepted
|
||
|
||
Recovery Coordinator связывает state, planning и REST Recovery,
|
||
не выполняя connect, disconnect или subscription restore.
|
||
|
||
### ADR-060.24-010 — Composition не запускает lifecycle
|
||
|
||
**Статус:** Accepted
|
||
|
||
Factory создаёт граф без network-, task- и configuration-side effects.
|
||
|
||
---
|
||
|
||
## 18. Проверяемые инварианты
|
||
|
||
### State
|
||
|
||
- checkpoint равен последней принятой сделке;
|
||
- checkpoint и `last_trade_id` согласованы;
|
||
- duplicate и error не изменяют checkpoint.
|
||
|
||
### Planning
|
||
|
||
- каждое окно валидно;
|
||
- окна не превышают configured maximum;
|
||
- окна покрывают полный диапазон без разрывов;
|
||
- равные границы дают пустой plan.
|
||
|
||
### Runtime
|
||
|
||
- Heartbeat публикует timeout однократно;
|
||
- Supervisor не запускает параллельный reconnect;
|
||
- Reconnect восстанавливает подписки только после connect;
|
||
- Scheduler не создаёт второй loop;
|
||
- ошибки не скрываются.
|
||
|
||
### Recovery
|
||
|
||
- отсутствие state/checkpoint не создаёт state;
|
||
- окна выполняются последовательно;
|
||
- результаты агрегируются в исходных границах;
|
||
- recovered Trade сохраняют identity;
|
||
- timezone и Unix millisecond conversion детерминированы.
|
||
|
||
### Composition
|
||
|
||
- live и recovery используют один consistency controller;
|
||
- coordinator и consistency controller используют один state store;
|
||
- runtime-компоненты получают ровно те зависимости, которые созданы
|
||
factory;
|
||
- создание графа не вызывает runtime-методы.
|
||
|
||
---
|
||
|
||
## 19. Тестовая стратегия
|
||
|
||
Build проверяется на трёх уровнях.
|
||
|
||
### Value objects и чистые вычисления
|
||
|
||
- checkpoint invariants;
|
||
- Recovery Window validation;
|
||
- Window Planner boundaries.
|
||
|
||
### Изолированные runtime-компоненты
|
||
|
||
- protocol compatibility;
|
||
- state transitions;
|
||
- event order;
|
||
- async interaction;
|
||
- error propagation;
|
||
- injected clock и sleep.
|
||
|
||
### Composition
|
||
|
||
- типы компонентов;
|
||
- identity общего Store и Consistency Controller;
|
||
- отсутствие side effects;
|
||
- передача конфигурации и внешних зависимостей.
|
||
|
||
Итоговый целевой набор:
|
||
|
||
```text
|
||
230 passed
|
||
```
|
||
|
||
Полная регрессия:
|
||
|
||
```text
|
||
1620 passed
|
||
```
|
||
|
||
End-to-end WebSocket и stress tests не подменяются unit-тестами
|
||
и остаются обязательной частью Build 060.25–060.26.
|
||
|
||
---
|
||
|
||
## 20. Граница Build 060.25
|
||
|
||
Build 060.25 должен добавить production-владельца lifecycle:
|
||
|
||
```text
|
||
bootstrap
|
||
│
|
||
▼
|
||
build composition
|
||
│
|
||
▼
|
||
connect and subscribe
|
||
│
|
||
▼
|
||
start supervisor and scheduler task
|
||
│
|
||
▼
|
||
receive messages and notify activity
|
||
│
|
||
▼
|
||
disconnect detected
|
||
│
|
||
▼
|
||
reconnect and restore subscriptions
|
||
│
|
||
▼
|
||
choose recovery_end_time
|
||
│
|
||
▼
|
||
RuntimeRecoveryCoordinator.recover()
|
||
│
|
||
▼
|
||
resume steady live lifecycle
|
||
```
|
||
|
||
До появления этой интеграции нельзя утверждать, что production
|
||
Trade Stream автоматически переживает WebSocket disconnect.
|
||
|
||
---
|
||
|
||
## 21. Что не входит в архитектуру Build 060.24
|
||
|
||
- production bootstrap;
|
||
- конкретный WebSocket lifecycle owner;
|
||
- автоматический вызов Runtime Recovery после reconnect;
|
||
- policy выбора `recovery_end_time`;
|
||
- retry/backoff;
|
||
- parallel recovery нескольких symbols;
|
||
- persistent checkpoint;
|
||
- долговременное Market Data Storage;
|
||
- historical query и replay services;
|
||
- integration, reconnect, recovery и stress tests полного процесса.
|
||
|
||
---
|
||
|
||
## 22. Итог
|
||
|
||
Build 060.24 формирует завершённый внутренний набор компонентов Runtime
|
||
Recovery и фиксирует их границы.
|
||
|
||
Архитектура:
|
||
|
||
- сохраняет checkpoint в единственном владельце;
|
||
- отделяет вычисление окон от выполнения Recovery;
|
||
- разделяет heartbeat, scheduling, supervision и reconnect;
|
||
- не смешивает Recovery с Transport;
|
||
- собирает live и recovery поверх общего Consistency Layer;
|
||
- не скрывает production lifecycle внутри factory-функции.
|
||
|
||
Следующий [Build 060.25](build_060_25_architecture.md) должен интегрировать
|
||
эту архитектуру, а не менять её ответственности.
|