1225 lines
37 KiB
Markdown
1225 lines
37 KiB
Markdown
# Build 060.22 — Acquisition Runtime Service
|
||
|
||
**Engineering Migration Report**
|
||
|
||
---
|
||
|
||
# Контроль документа
|
||
|
||
| Свойство | Значение |
|
||
|----------|----------|
|
||
| Build | 060.22 |
|
||
| Название | Acquisition Runtime Service |
|
||
| Статус | Completed |
|
||
| Проект | Dzentra |
|
||
| Подсистема | Market Data Acquisition |
|
||
| Компонент | Acquisition Runtime Service Layer |
|
||
| Версия | 1.0 |
|
||
|
||
---
|
||
|
||
# Связанные документы
|
||
|
||
- `build_060_22_architecture.md` — архитектурная спецификация Build.
|
||
- `build_060_21.md` — Engineering Migration Report предыдущего Build.
|
||
- `build_060_21_architecture.md` — спецификация Runtime Integration Contracts.
|
||
- `build_060_20_1.md` — Engineering Migration Report корректирующего Build владения состоянием Trade Stream.
|
||
|
||
---
|
||
|
||
# Цель Build
|
||
|
||
Build 060.21 завершил формирование контрактного слоя Acquisition Runtime.
|
||
|
||
После предыдущего этапа в системе уже существовали:
|
||
|
||
```text
|
||
AcquisitionRuntimeCommand
|
||
AcquisitionRuntimeEvent
|
||
AcquisitionRuntimeCommandDispatcherProtocol
|
||
AcquisitionRuntimeEventPublisherProtocol
|
||
```
|
||
|
||
Также были доступны базовые WebSocket-контракты:
|
||
|
||
```text
|
||
WebSocketTransportProtocol
|
||
WebSocketSessionProtocol
|
||
WebSocketSubscriptionManagerProtocol
|
||
```
|
||
|
||
Однако Runtime Layer оставался декларативным.
|
||
|
||
Команды описывали инфраструктурные намерения, но в системе отсутствовал производственный компонент, который:
|
||
|
||
- принимал типизированную Runtime-команду;
|
||
- определял её конкретный тип;
|
||
- выбирал соответствующую инфраструктурную зависимость;
|
||
- выполнял одну детерминированную операцию;
|
||
- сохранял независимость от Trades Feed, Consistency и Recovery.
|
||
|
||
Главной задачей Build 060.22 стало создание первого исполняемого сервисного компонента Acquisition Runtime:
|
||
|
||
```text
|
||
AcquisitionRuntimeService
|
||
```
|
||
|
||
Одновременно был введён отдельный публичный контракт сервисного уровня:
|
||
|
||
```text
|
||
AcquisitionRuntimeServiceProtocol
|
||
```
|
||
|
||
После завершения Build система получает:
|
||
|
||
- единый сервис исполнения Acquisition Runtime Commands;
|
||
- детерминированную маршрутизацию всех шести существующих команд;
|
||
- отдельный публичный Protocol сервиса;
|
||
- расширенный контракт управления WebSocket-подписками;
|
||
- полное unit-покрытие нового сервисного слоя;
|
||
- подтверждённую регрессионную совместимость Runtime, Consistency, Recovery и Feeds.
|
||
|
||
При этом Build сознательно не выполняет интеграцию Runtime Service в корневой Acquisition Service или Trades Feed.
|
||
|
||
Эта задача относится к Build 060.23.
|
||
|
||
---
|
||
|
||
# Предпосылки
|
||
|
||
К началу настоящего Build Runtime Layer уже обладал полным набором неизменяемых команд:
|
||
|
||
```text
|
||
ConnectCommand
|
||
DisconnectCommand
|
||
SubscribeCommand
|
||
UnsubscribeCommand
|
||
SendTextCommand
|
||
SendBinaryCommand
|
||
```
|
||
|
||
Также существовали инфраструктурные события:
|
||
|
||
```text
|
||
ConnectedEvent
|
||
DisconnectedEvent
|
||
ConnectFailedEvent
|
||
MessageReceivedEvent
|
||
MessageSentEvent
|
||
ReconnectStartedEvent
|
||
ReconnectCompletedEvent
|
||
ReconnectFailedEvent
|
||
HeartbeatTimeoutEvent
|
||
```
|
||
|
||
Build 060.21 определил публичные границы передачи команд и событий, но сознательно не создавал исполняемую реализацию.
|
||
|
||
Архитектура выглядела следующим образом:
|
||
|
||
```text
|
||
AcquisitionRuntimeCommand
|
||
│
|
||
▼
|
||
AcquisitionRuntimeCommandDispatcherProtocol
|
||
│
|
||
▼
|
||
(реализация отсутствует)
|
||
```
|
||
|
||
Таким образом следующий этап должен был закрыть именно сервисную границу между типизированными командами и существующими инфраструктурными компонентами.
|
||
|
||
---
|
||
|
||
# Результаты архитектурного аудита
|
||
|
||
Перед началом реализации был выполнен аудит следующих компонентов:
|
||
|
||
```text
|
||
src/market_data/acquisition/runtime/websocket_protocol.py
|
||
src/market_data/acquisition/runtime/runtime_commands.py
|
||
src/market_data/acquisition/runtime/runtime_events.py
|
||
src/market_data/acquisition/runtime/transport_messages.py
|
||
src/market_data/acquisition/runtime/supervisor.py
|
||
src/market_data/acquisition/runtime/reconnect.py
|
||
src/market_data/acquisition/runtime/scheduler.py
|
||
src/market_data/acquisition/runtime/heartbeat.py
|
||
```
|
||
|
||
Дополнительно были проанализированы:
|
||
|
||
```text
|
||
src/market_data/acquisition/service.py
|
||
src/market_data/acquisition/protocol.py
|
||
src/market_data/acquisition/registry.py
|
||
```
|
||
|
||
а также соответствующие unit-тесты.
|
||
|
||
Аудит подтвердил следующее.
|
||
|
||
## Runtime orchestration ещё не реализован
|
||
|
||
Файлы:
|
||
|
||
```text
|
||
supervisor.py
|
||
reconnect.py
|
||
scheduler.py
|
||
heartbeat.py
|
||
```
|
||
|
||
не содержали производственной реализации.
|
||
|
||
Следовательно Build 060.22 не являлся рефакторингом существующего Runtime Service.
|
||
|
||
Он создавал первую минимальную сервисную реализацию с нуля.
|
||
|
||
---
|
||
|
||
## Supervisor преждевременен
|
||
|
||
На раннем этапе рассматривалась возможность реализовать сервис внутри:
|
||
|
||
```text
|
||
supervisor.py
|
||
```
|
||
|
||
Данный вариант был отклонён.
|
||
|
||
Supervisor является компонентом более высокого уровня и должен координировать:
|
||
|
||
- Runtime Service;
|
||
- reconnect;
|
||
- heartbeat;
|
||
- scheduler;
|
||
- lifecycle длительно работающей подсистемы.
|
||
|
||
Поскольку перечисленные механизмы ещё не реализованы, использование Supervisor в Build 060.22 создало бы преждевременную orchestration-логику.
|
||
|
||
Поэтому был введён отдельный тонкий сервис:
|
||
|
||
```text
|
||
AcquisitionRuntimeService
|
||
```
|
||
|
||
---
|
||
|
||
## Registry для Runtime Service не требуется
|
||
|
||
Существующие Feed Registry предназначены для выбора одной реализации из нескольких зарегистрированных источников.
|
||
|
||
Runtime Service в текущей архитектуре существует в единственном экземпляре и не представляет семейство взаимозаменяемых Feed-реализаций.
|
||
|
||
Создание отдельного Runtime Registry было признано искусственным и исключено из Scope Build.
|
||
|
||
---
|
||
|
||
## Корневые Acquisition-файлы не изменяются
|
||
|
||
Аудит подтвердил отсутствие необходимости изменять:
|
||
|
||
```text
|
||
src/market_data/acquisition/service.py
|
||
src/market_data/acquisition/protocol.py
|
||
src/market_data/acquisition/registry.py
|
||
```
|
||
|
||
Интеграция нового Runtime Service с Acquisition Layer относится к следующему этапу:
|
||
|
||
```text
|
||
Build 060.23 — Acquisition Integration
|
||
```
|
||
|
||
---
|
||
|
||
# Архитектурное решение
|
||
|
||
По результатам аудита была утверждена следующая модель.
|
||
|
||
```text
|
||
AcquisitionRuntimeCommand
|
||
│
|
||
▼
|
||
AcquisitionRuntimeServiceProtocol
|
||
│
|
||
▼
|
||
AcquisitionRuntimeService
|
||
│
|
||
┌─────┼──────────────┐
|
||
▼ ▼ ▼
|
||
Session Transport Subscription Manager
|
||
```
|
||
|
||
`AcquisitionRuntimeService` реализует одновременно:
|
||
|
||
```text
|
||
AcquisitionRuntimeServiceProtocol
|
||
AcquisitionRuntimeCommandDispatcherProtocol
|
||
```
|
||
|
||
Сервис не реализует WebSocket самостоятельно.
|
||
|
||
Он только делегирует каждую команду одной заранее определённой инфраструктурной зависимости.
|
||
|
||
---
|
||
|
||
# Правило уникальности имён файлов
|
||
|
||
Build 060.22 продолжает обязательное архитектурное правило проекта Dzentra:
|
||
|
||
> Во всём репозитории не допускается создание нескольких новых файлов с одинаковым именем независимо от их расположения в каталогах.
|
||
|
||
Единственным служебным исключением остаётся:
|
||
|
||
```text
|
||
__init__.py
|
||
```
|
||
|
||
Поэтому Build сознательно не создаёт файлы с общими именами:
|
||
|
||
```text
|
||
service.py
|
||
protocol.py
|
||
test_service.py
|
||
```
|
||
|
||
Вместо них добавлены уникальные имена:
|
||
|
||
```text
|
||
acquisition_runtime_service.py
|
||
acquisition_runtime_service_protocol.py
|
||
test_acquisition_runtime_service.py
|
||
```
|
||
|
||
Имена однозначно отражают назначение файлов без необходимости учитывать родительский каталог.
|
||
|
||
Repository-wide поиск показал, что в проекте уже существуют старые файлы с общими именами, созданные до введения данного правила.
|
||
|
||
Build 060.22 не добавляет новых файлов с такими именами и не выполняет ретроспективное переименование существующего рабочего кода вне согласованного Scope.
|
||
|
||
---
|
||
|
||
# AcquisitionRuntimeServiceProtocol
|
||
|
||
В рамках Build создан новый публичный контракт:
|
||
|
||
```text
|
||
AcquisitionRuntimeServiceProtocol
|
||
```
|
||
|
||
Файл:
|
||
|
||
```text
|
||
src/market_data/acquisition/runtime/
|
||
acquisition_runtime_service_protocol.py
|
||
```
|
||
|
||
Protocol определяет единственную публичную операцию:
|
||
|
||
```python
|
||
async def dispatch(
|
||
command: AcquisitionRuntimeCommand,
|
||
) -> None
|
||
```
|
||
|
||
Другие публичные методы не вводятся.
|
||
|
||
В частности отсутствуют:
|
||
|
||
```text
|
||
connect()
|
||
disconnect()
|
||
subscribe()
|
||
unsubscribe()
|
||
send()
|
||
```
|
||
|
||
Все операции Runtime выражаются исключительно через типизированные команды.
|
||
|
||
Это исключает появление второго параллельного API для одной и той же инфраструктурной операции.
|
||
|
||
---
|
||
|
||
# Почему Service Protocol существует отдельно
|
||
|
||
В Build 060.21 уже был добавлен:
|
||
|
||
```text
|
||
AcquisitionRuntimeCommandDispatcherProtocol
|
||
```
|
||
|
||
Он определяет инфраструктурную способность принимать команды.
|
||
|
||
Build 060.22 дополнительно вводит:
|
||
|
||
```text
|
||
AcquisitionRuntimeServiceProtocol
|
||
```
|
||
|
||
Данный контракт фиксирует отдельную сервисную границу Runtime Layer.
|
||
|
||
Такое разделение позволяет последующим компонентам зависеть именно от сервисной абстракции, а не от конкретной реализации.
|
||
|
||
В будущем под тем же Protocol могут существовать различные реализации:
|
||
|
||
```text
|
||
Simple Acquisition Runtime Service
|
||
Queued Acquisition Runtime Service
|
||
Supervised Acquisition Runtime Service
|
||
```
|
||
|
||
Build 060.22 реализует только минимальный прямой вариант.
|
||
|
||
---
|
||
|
||
# AcquisitionRuntimeService
|
||
|
||
Главным production-компонентом Build становится:
|
||
|
||
```text
|
||
AcquisitionRuntimeService
|
||
```
|
||
|
||
Файл:
|
||
|
||
```text
|
||
src/market_data/acquisition/runtime/
|
||
acquisition_runtime_service.py
|
||
```
|
||
|
||
Сервис является тонким stateless-координатором.
|
||
|
||
Он:
|
||
|
||
- принимает одну типизированную команду;
|
||
- определяет её конкретный тип;
|
||
- вызывает соответствующую инфраструктурную зависимость;
|
||
- завершает выполнение после завершения делегированной операции.
|
||
|
||
Сервис не:
|
||
|
||
- создаёт фоновые задачи;
|
||
- хранит очередь команд;
|
||
- выполняет retries;
|
||
- управляет reconnect;
|
||
- запускает heartbeat;
|
||
- хранит подписки;
|
||
- обрабатывает Trade;
|
||
- вызывает Consistency;
|
||
- вызывает Recovery.
|
||
|
||
---
|
||
|
||
# Зависимости сервиса
|
||
|
||
Все зависимости передаются через конструктор.
|
||
|
||
```text
|
||
AcquisitionRuntimeService
|
||
│
|
||
├── WebSocketSessionProtocol
|
||
├── WebSocketTransportProtocol
|
||
├── WebSocketSubscriptionManagerProtocol
|
||
└── AcquisitionRuntimeEventPublisherProtocol
|
||
```
|
||
|
||
Сервис не создаёт зависимости самостоятельно.
|
||
|
||
Создание графа объектов относится к Composition Root и не входит в Scope Build 060.22.
|
||
|
||
---
|
||
|
||
# Почему Event Publisher внедрён, но пока не используется
|
||
|
||
Конструктор Runtime Service принимает:
|
||
|
||
```text
|
||
AcquisitionRuntimeEventPublisherProtocol
|
||
```
|
||
|
||
Однако Build 060.22 не публикует события после выполнения команд.
|
||
|
||
Это осознанное решение.
|
||
|
||
Publisher включён в окончательную форму конструктора, чтобы последующие Build могли подключить публикацию событий без изменения публичной композиции сервиса.
|
||
|
||
При этом преждевременная event-orchestration не добавляется.
|
||
|
||
Unit-тест отдельно подтверждает, что Publisher пока остаётся неиспользованным.
|
||
|
||
---
|
||
|
||
# Расширение WebSocketSubscriptionManagerProtocol
|
||
|
||
Во время реализации было обнаружено расхождение между первоначальной архитектурной матрицей и фактическим контрактом Subscription Manager.
|
||
|
||
До Build Protocol содержал только:
|
||
|
||
```python
|
||
restore_subscriptions()
|
||
clear_subscriptions()
|
||
```
|
||
|
||
Но Runtime Service должен исполнять:
|
||
|
||
```text
|
||
SubscribeCommand
|
||
UnsubscribeCommand
|
||
```
|
||
|
||
Следовательно существующий контракт был недостаточен для детерминированной маршрутизации.
|
||
|
||
В Build `WebSocketSubscriptionManagerProtocol` локально расширен методами:
|
||
|
||
```python
|
||
async def subscribe(
|
||
subscription_key: str,
|
||
message: AcquisitionSubscriptionMessage,
|
||
) -> None
|
||
```
|
||
|
||
и:
|
||
|
||
```python
|
||
async def unsubscribe(
|
||
subscription_key: str,
|
||
message: AcquisitionSubscriptionMessage,
|
||
) -> None
|
||
```
|
||
|
||
Существующие методы:
|
||
|
||
```text
|
||
restore_subscriptions()
|
||
clear_subscriptions()
|
||
```
|
||
|
||
полностью сохранены.
|
||
|
||
Расширение является обратно совместимым с точки зрения существующих моделей данных и не изменяет поведение уже реализованных компонентов.
|
||
|
||
---
|
||
|
||
# AcquisitionSubscriptionMessage
|
||
|
||
Для операций подписки введён типовой alias:
|
||
|
||
```text
|
||
AcquisitionSubscriptionMessage
|
||
```
|
||
|
||
Он объединяет:
|
||
|
||
```text
|
||
TransportTextMessage
|
||
TransportBinaryMessage
|
||
```
|
||
|
||
Благодаря этому Subscription Manager принимает не произвольные `str | bytes`, а уже существующие неизменяемые модели транспортных сообщений.
|
||
|
||
Это обеспечивает соответствие моделям:
|
||
|
||
```text
|
||
SubscribeCommand
|
||
UnsubscribeCommand
|
||
```
|
||
|
||
и исключает повторную интерпретацию payload внутри Runtime Service.
|
||
|
||
---
|
||
|
||
# Официальная матрица маршрутизации
|
||
|
||
Build 060.22 реализует следующую детерминированную таблицу.
|
||
|
||
| Runtime Command | Действие |
|
||
|-----------------|----------|
|
||
| `ConnectCommand` | `WebSocketSessionProtocol.start()` |
|
||
| `DisconnectCommand` | `WebSocketSessionProtocol.stop()` |
|
||
| `SubscribeCommand` | `WebSocketSubscriptionManagerProtocol.subscribe(...)` |
|
||
| `UnsubscribeCommand` | `WebSocketSubscriptionManagerProtocol.unsubscribe(...)` |
|
||
| `SendTextCommand` | `WebSocketTransportProtocol.send(message.payload)` |
|
||
| `SendBinaryCommand` | `WebSocketTransportProtocol.send(message.payload)` |
|
||
|
||
Каждый тип команды имеет ровно один маршрут.
|
||
|
||
Сервис не выполняет дополнительные действия и не вызывает несколько зависимостей для одной команды.
|
||
|
||
---
|
||
|
||
# ConnectCommand
|
||
|
||
Маршрут:
|
||
|
||
```text
|
||
ConnectCommand
|
||
│
|
||
▼
|
||
WebSocketSessionProtocol.start()
|
||
```
|
||
|
||
Runtime Service не вызывает `transport.connect()` напрямую.
|
||
|
||
Жизненный цикл соединения принадлежит Session.
|
||
|
||
---
|
||
|
||
# DisconnectCommand
|
||
|
||
Маршрут:
|
||
|
||
```text
|
||
DisconnectCommand
|
||
│
|
||
▼
|
||
WebSocketSessionProtocol.stop()
|
||
```
|
||
|
||
Runtime Service не вызывает `transport.disconnect()` напрямую.
|
||
|
||
Завершение сессии остаётся обязанностью Session.
|
||
|
||
---
|
||
|
||
# SubscribeCommand
|
||
|
||
Маршрут:
|
||
|
||
```text
|
||
SubscribeCommand
|
||
│
|
||
▼
|
||
WebSocketSubscriptionManagerProtocol.subscribe(
|
||
subscription_key,
|
||
message,
|
||
)
|
||
```
|
||
|
||
Runtime Service передаёт исходные значения без изменения.
|
||
|
||
Сервис не хранит список подписок и не сериализует сообщение.
|
||
|
||
---
|
||
|
||
# UnsubscribeCommand
|
||
|
||
Маршрут:
|
||
|
||
```text
|
||
UnsubscribeCommand
|
||
│
|
||
▼
|
||
WebSocketSubscriptionManagerProtocol.unsubscribe(
|
||
subscription_key,
|
||
message,
|
||
)
|
||
```
|
||
|
||
Сервис не изменяет внутреннее состояние Subscription Manager самостоятельно.
|
||
|
||
---
|
||
|
||
# SendTextCommand
|
||
|
||
Маршрут:
|
||
|
||
```text
|
||
SendTextCommand
|
||
│
|
||
▼
|
||
WebSocketTransportProtocol.send(str)
|
||
```
|
||
|
||
В Transport передаётся:
|
||
|
||
```text
|
||
command.message.payload
|
||
```
|
||
|
||
Runtime Service не вводит отдельный метод `send_text()` и не расширяет Transport Protocol без необходимости.
|
||
|
||
---
|
||
|
||
# SendBinaryCommand
|
||
|
||
Маршрут:
|
||
|
||
```text
|
||
SendBinaryCommand
|
||
│
|
||
▼
|
||
WebSocketTransportProtocol.send(bytes)
|
||
```
|
||
|
||
В Transport передаётся бинарный payload без преобразования.
|
||
|
||
Существующий единый метод:
|
||
|
||
```python
|
||
send(message: str | bytes)
|
||
```
|
||
|
||
полностью покрывает оба типа сообщений.
|
||
|
||
---
|
||
|
||
# Обработка неизвестной команды
|
||
|
||
Тип `AcquisitionRuntimeCommand` ограничивает штатный публичный вход сервиса.
|
||
|
||
Однако Runtime Service также содержит защитную ветку для произвольного неподдерживаемого объекта.
|
||
|
||
В этом случае генерируется:
|
||
|
||
```text
|
||
TypeError
|
||
```
|
||
|
||
с диагностическим сообщением, включающим имя фактического типа.
|
||
|
||
Это гарантирует, что появление новой Runtime-команды потребует явного обновления маршрутизации и не останется незамеченным.
|
||
|
||
---
|
||
|
||
# Обработка исключений
|
||
|
||
Build не вводит собственную иерархию ошибок Runtime Service.
|
||
|
||
Любое исключение инфраструктурной зависимости распространяется вызывающему компоненту без обёртки.
|
||
|
||
Например:
|
||
|
||
```text
|
||
ConnectCommand
|
||
│
|
||
▼
|
||
AcquisitionRuntimeService
|
||
│
|
||
▼
|
||
WebSocketSession.start()
|
||
│
|
||
▼
|
||
RuntimeError
|
||
```
|
||
|
||
Сервис не преобразует ошибку в общий `RuntimeDispatchError`.
|
||
|
||
Такое решение сохраняет:
|
||
|
||
- исходный тип исключения;
|
||
- исходное диагностическое сообщение;
|
||
- прозрачность инфраструктурного поведения;
|
||
- возможность точной обработки на более высоком уровне.
|
||
|
||
---
|
||
|
||
# Stateless-архитектура сервиса
|
||
|
||
`AcquisitionRuntimeService` не хранит собственное операционное состояние.
|
||
|
||
Внутри объекта сохраняются только внедрённые зависимости.
|
||
|
||
Сервис не хранит:
|
||
|
||
- состояние соединения;
|
||
- выполненные команды;
|
||
- активные подписки;
|
||
- историю отправленных сообщений;
|
||
- reconnect attempts;
|
||
- Recovery state;
|
||
- Consistency state.
|
||
|
||
Все перечисленные данные принадлежат специализированным компонентам.
|
||
|
||
---
|
||
|
||
# Изменённые файлы
|
||
|
||
## Новый Service Protocol
|
||
|
||
```text
|
||
src/market_data/acquisition/runtime/
|
||
acquisition_runtime_service_protocol.py
|
||
```
|
||
|
||
Добавлен:
|
||
|
||
```text
|
||
AcquisitionRuntimeServiceProtocol
|
||
```
|
||
|
||
Protocol содержит единственный асинхронный метод `dispatch()`.
|
||
|
||
---
|
||
|
||
## Новый Runtime Service
|
||
|
||
```text
|
||
src/market_data/acquisition/runtime/
|
||
acquisition_runtime_service.py
|
||
```
|
||
|
||
Добавлен:
|
||
|
||
```text
|
||
AcquisitionRuntimeService
|
||
```
|
||
|
||
Класс реализует:
|
||
|
||
```text
|
||
AcquisitionRuntimeServiceProtocol
|
||
AcquisitionRuntimeCommandDispatcherProtocol
|
||
```
|
||
|
||
и маршрутизирует все шесть Runtime Commands.
|
||
|
||
---
|
||
|
||
## Расширение WebSocket Protocol Layer
|
||
|
||
```text
|
||
src/market_data/acquisition/runtime/websocket_protocol.py
|
||
```
|
||
|
||
Добавлены:
|
||
|
||
```text
|
||
AcquisitionSubscriptionMessage
|
||
```
|
||
|
||
и методы:
|
||
|
||
```text
|
||
WebSocketSubscriptionManagerProtocol.subscribe()
|
||
WebSocketSubscriptionManagerProtocol.unsubscribe()
|
||
```
|
||
|
||
Существующие Protocol и alias сохранены.
|
||
|
||
---
|
||
|
||
## Unit-тест Runtime Service
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/runtime/
|
||
test_acquisition_runtime_service.py
|
||
```
|
||
|
||
Добавлены тестовые реализации:
|
||
|
||
- `FakeSession`;
|
||
- `FakeTransport`;
|
||
- `FakeSubscriptionManager`;
|
||
- `FakeEventPublisher`.
|
||
|
||
Проверена маршрутизация всех поддерживаемых команд и распространение исключений.
|
||
|
||
---
|
||
|
||
## Обновление теста WebSocket Protocol
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/runtime/
|
||
test_websocket_protocol.py
|
||
```
|
||
|
||
`FakeWebSocketSubscriptionManager` расширен методами:
|
||
|
||
```text
|
||
subscribe()
|
||
unsubscribe()
|
||
```
|
||
|
||
Также добавлена негативная проверка неполной реализации обновлённого Protocol.
|
||
|
||
---
|
||
|
||
# Файлы, которые не изменялись
|
||
|
||
Build не изменяет:
|
||
|
||
```text
|
||
src/market_data/acquisition/runtime/runtime_commands.py
|
||
src/market_data/acquisition/runtime/runtime_events.py
|
||
src/market_data/acquisition/runtime/transport_messages.py
|
||
src/market_data/acquisition/runtime/supervisor.py
|
||
src/market_data/acquisition/runtime/reconnect.py
|
||
src/market_data/acquisition/runtime/scheduler.py
|
||
src/market_data/acquisition/runtime/heartbeat.py
|
||
```
|
||
|
||
Также не изменяются:
|
||
|
||
- корневой Acquisition Service;
|
||
- Acquisition Registry;
|
||
- Acquisition Protocol;
|
||
- Trades Feed;
|
||
- Quotes Feed;
|
||
- Candles Feed;
|
||
- Trade Stream Consistency;
|
||
- Trade Stream State Store;
|
||
- Trade Recovery;
|
||
- WebSocket adapters;
|
||
- каноническая модель `Trade`.
|
||
|
||
---
|
||
|
||
# Unit-тестирование AcquisitionRuntimeService
|
||
|
||
Новый сервис покрыт отдельным набором unit-тестов.
|
||
|
||
Проверены следующие сценарии:
|
||
|
||
- сервис соответствует `AcquisitionRuntimeServiceProtocol`;
|
||
- сервис соответствует `AcquisitionRuntimeCommandDispatcherProtocol`;
|
||
- `ConnectCommand` вызывает только `session.start()`;
|
||
- `DisconnectCommand` вызывает только `session.stop()`;
|
||
- `SubscribeCommand` передаёт исходные `subscription_key` и message;
|
||
- `UnsubscribeCommand` передаёт исходные `subscription_key` и message;
|
||
- `SendTextCommand` передаёт строковый payload в `transport.send()`;
|
||
- `SendBinaryCommand` передаёт бинарный payload в `transport.send()`;
|
||
- Event Publisher не используется преждевременно;
|
||
- неизвестная команда приводит к `TypeError`;
|
||
- исключение Session распространяется без изменения.
|
||
|
||
Результат:
|
||
|
||
```text
|
||
11 passed
|
||
```
|
||
|
||
---
|
||
|
||
# Unit-тестирование WebSocket Protocol
|
||
|
||
После расширения Subscription Manager выполнен локальный прогон обновлённого Protocol Layer.
|
||
|
||
Проверены:
|
||
|
||
- полная реализация Transport;
|
||
- полная реализация Session;
|
||
- полная реализация Subscription Manager;
|
||
- Command Dispatcher;
|
||
- Event Publisher;
|
||
- отрицательные сценарии неполных реализаций.
|
||
|
||
Результат:
|
||
|
||
```text
|
||
9 passed
|
||
```
|
||
|
||
---
|
||
|
||
# Регрессионное тестирование Runtime Layer
|
||
|
||
После добавления Runtime Service выполнен полный прогон:
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/runtime
|
||
```
|
||
|
||
Проверены:
|
||
|
||
- Acquisition Runtime Service;
|
||
- Runtime Commands;
|
||
- Runtime Events;
|
||
- Transport Messages;
|
||
- WebSocket Protocol Layer.
|
||
|
||
Результат:
|
||
|
||
```text
|
||
46 passed
|
||
```
|
||
|
||
Это подтверждает, что новый сервисный слой не нарушил существующее поведение Runtime.
|
||
|
||
---
|
||
|
||
# Расширенное регрессионное тестирование
|
||
|
||
Для подтверждения архитектурной изоляции выполнен совместный прогон:
|
||
|
||
```text
|
||
Runtime
|
||
Consistency
|
||
Recovery
|
||
Feeds
|
||
```
|
||
|
||
Команда охватывала каталоги:
|
||
|
||
```text
|
||
tests/unit/market_data/acquisition/runtime
|
||
tests/unit/market_data/acquisition/consistency
|
||
tests/unit/market_data/acquisition/recovery
|
||
tests/unit/market_data/acquisition/feeds
|
||
```
|
||
|
||
Результат:
|
||
|
||
```text
|
||
180 passed
|
||
```
|
||
|
||
Тем самым подтверждено:
|
||
|
||
- Runtime Service работает корректно;
|
||
- обновление Subscription Manager не нарушило Protocol Layer;
|
||
- Trade Stream Consistency не затронута;
|
||
- Trade Stream State Store не затронут;
|
||
- Trade Recovery не затронута;
|
||
- существующие Feed продолжают работать без изменений;
|
||
- преждевременная интеграция Build 060.23 не произошла.
|
||
|
||
---
|
||
|
||
# Обратная совместимость
|
||
|
||
Build сохраняет функциональное поведение существующих подсистем.
|
||
|
||
Не изменились:
|
||
|
||
- Runtime Command models;
|
||
- Runtime Event models;
|
||
- Transport Message models;
|
||
- WebSocket Transport API;
|
||
- WebSocket Session API;
|
||
- Canonical Trade Model;
|
||
- Trades Feed API;
|
||
- Consistency API;
|
||
- Recovery API;
|
||
- Feed Registry;
|
||
- корневые Acquisition Services.
|
||
|
||
Единственное расширение существующего контракта относится к `WebSocketSubscriptionManagerProtocol` и добавляет операции, уже необходимые существующим `SubscribeCommand` и `UnsubscribeCommand`.
|
||
|
||
---
|
||
|
||
# Производительность
|
||
|
||
Runtime Service выполняет конечную цепочку `isinstance`-проверок по шести типам команд и один делегированный асинхронный вызов.
|
||
|
||
Сервис не вводит:
|
||
|
||
- очереди;
|
||
- дополнительные копии payload;
|
||
- сериализацию;
|
||
- фоновые задачи;
|
||
- блокировки;
|
||
- retries;
|
||
- хранение истории.
|
||
|
||
Следовательно накладные расходы ограничены постоянным временем:
|
||
|
||
```text
|
||
O(1)
|
||
```
|
||
|
||
для каждой команды.
|
||
|
||
Основная задержка полностью определяется вызываемой инфраструктурной зависимостью.
|
||
|
||
---
|
||
|
||
# Подтверждённые архитектурные инварианты
|
||
|
||
## Runtime Service не знает предметную область
|
||
|
||
Сервис не импортирует:
|
||
|
||
```text
|
||
Trade
|
||
TradesFeed
|
||
TradeStreamConsistencyProtocol
|
||
TradeRecoveryProtocol
|
||
TradeStreamStateStore
|
||
```
|
||
|
||
---
|
||
|
||
## Runtime Service stateless
|
||
|
||
Сервис хранит только ссылки на внедрённые зависимости.
|
||
|
||
---
|
||
|
||
## Одна команда имеет один маршрут
|
||
|
||
Ни одна команда не выполняет несколько инфраструктурных операций.
|
||
|
||
---
|
||
|
||
## Session владеет lifecycle соединения
|
||
|
||
`ConnectCommand` и `DisconnectCommand` делегируются Session, а не Transport напрямую.
|
||
|
||
---
|
||
|
||
## Subscription Manager владеет подписками
|
||
|
||
Runtime Service не хранит список подписок и не управляет их состоянием самостоятельно.
|
||
|
||
---
|
||
|
||
## Transport владеет отправкой payload
|
||
|
||
Text и Binary payload передаются через существующий единый метод `send()`.
|
||
|
||
---
|
||
|
||
## Event publication не реализована преждевременно
|
||
|
||
Publisher внедрён, но не используется до отдельного этапа интеграции событий.
|
||
|
||
---
|
||
|
||
## Consistency и Recovery не зависят от Runtime Service
|
||
|
||
Новые импорты и зависимости в данных подсистемах отсутствуют.
|
||
|
||
---
|
||
|
||
## Уникальность новых имён файлов соблюдена
|
||
|
||
Build не добавляет новых `service.py`, `protocol.py` или `test_service.py`.
|
||
|
||
---
|
||
|
||
# Что не входит в Scope Build
|
||
|
||
Настоящий Build сознательно не реализует:
|
||
|
||
- интеграцию Runtime Service в корневой Acquisition Service;
|
||
- интеграцию Runtime Service с Trades Feed;
|
||
- Composition Root;
|
||
- Runtime Supervisor;
|
||
- Reconnect orchestration;
|
||
- Heartbeat;
|
||
- Scheduler;
|
||
- очередь команд;
|
||
- background worker;
|
||
- публикацию Runtime Events;
|
||
- обработку входящих WebSocket-сообщений;
|
||
- маршрутизацию сообщений к Trade Adapter;
|
||
- запуск Trade Stream Consistency;
|
||
- автоматический Trade Recovery;
|
||
- восстановление подписок после reconnect;
|
||
- реальный WebSocket transport implementation;
|
||
- сохранение Runtime-состояния между перезапусками.
|
||
|
||
Отсутствие перечисленных компонентов является осознанной границей Build и не рассматривается как незавершённость.
|
||
|
||
---
|
||
|
||
# Архитектурное значение Build
|
||
|
||
Build 060.22 является первым этапом серии Runtime, добавляющим исполняемое поведение.
|
||
|
||
До него Runtime Layer содержал:
|
||
|
||
- модели сообщений;
|
||
- команды;
|
||
- события;
|
||
- Protocol.
|
||
|
||
После завершения Build появляется единая точка выполнения команд:
|
||
|
||
```text
|
||
AcquisitionRuntimeService
|
||
```
|
||
|
||
Это превращает Runtime из исключительно декларативного слоя в минимальную работающую сервисную подсистему.
|
||
|
||
При этом сервис остаётся полностью изолированным от предметной области и готов к последующей интеграции без изменения уже реализованных Consistency, Recovery и Feed.
|
||
|
||
---
|
||
|
||
# Связь с последующими Build
|
||
|
||
## Build 060.23 — Acquisition Integration
|
||
|
||
Новый Runtime Service будет подключён к верхнему уровню Acquisition и станет доступен через утверждённую композицию зависимостей.
|
||
|
||
---
|
||
|
||
## Build 060.24 — Reconnect & Runtime Recovery
|
||
|
||
На основе Runtime Service будут построены:
|
||
|
||
- reconnect orchestration;
|
||
- resubscribe;
|
||
- восстановление Runtime-потока;
|
||
- вызов Trade Recovery после нарушения непрерывности.
|
||
|
||
---
|
||
|
||
## Build 060.25 — Integration & Regression
|
||
|
||
Будет выполнена полная проверка взаимодействия Runtime, Acquisition, Trades Feed, Consistency и Recovery.
|
||
|
||
---
|
||
|
||
# Заключение
|
||
|
||
Build 060.22 завершает создание минимального исполняемого сервисного слоя Acquisition Runtime.
|
||
|
||
В рамках этапа были реализованы:
|
||
|
||
```text
|
||
AcquisitionRuntimeServiceProtocol
|
||
AcquisitionRuntimeService
|
||
```
|
||
|
||
а также расширен:
|
||
|
||
```text
|
||
WebSocketSubscriptionManagerProtocol
|
||
```
|
||
|
||
для поддержки существующих команд подписки и отмены подписки.
|
||
|
||
Runtime Service детерминированно маршрутизирует все шесть команд к соответствующим инфраструктурным зависимостям и не содержит знаний о рыночных данных.
|
||
|
||
Архитектура сохраняет строгие границы:
|
||
|
||
- Session управляет lifecycle;
|
||
- Transport отправляет payload;
|
||
- Subscription Manager управляет подписками;
|
||
- Runtime Service только координирует;
|
||
- Consistency и Recovery остаются независимыми.
|
||
|
||
Все изменения подтверждены локальными и расширенными regression-тестами.
|
||
|
||
---
|
||
|
||
# Итог Build
|
||
|
||
После завершения Build 060.22 система обладает следующими возможностями.
|
||
|
||
✓ Создан отдельный публичный `AcquisitionRuntimeServiceProtocol`.
|
||
|
||
✓ Реализован `AcquisitionRuntimeService`.
|
||
|
||
✓ Поддерживаются все шесть существующих Acquisition Runtime Commands.
|
||
|
||
✓ Каждая команда имеет один детерминированный маршрут.
|
||
|
||
✓ `WebSocketSubscriptionManagerProtocol` поддерживает subscribe и unsubscribe.
|
||
|
||
✓ Text и Binary payload отправляются через существующий Transport API.
|
||
|
||
✓ Исключения инфраструктурных зависимостей распространяются без изменения.
|
||
|
||
✓ Event Publisher не используется преждевременно.
|
||
|
||
✓ Все новые файлы имеют уникальные имена.
|
||
|
||
✓ Runtime regression завершён результатом `46 passed`.
|
||
|
||
✓ Расширенный regression завершён результатом `180 passed`.
|
||
|
||
Build **060.22 — Acquisition Runtime Service** считается полностью завершённым и готовым к фиксации в Git.
|