13 KiB
Build 059.12 — Runtime Event Models
Migration Build
Цель Build
После завершения Build 059.11 Runtime уже содержит три независимых архитектурных уровня:
- Runtime Protocols;
- Runtime Transport Messages;
- Runtime Commands.
Следующим шагом является создание слоя Runtime Events.
Данный Build вводит immutable-модели событий Runtime.
События описывают уже произошедшие факты и полностью отделены от команд Runtime.
Причина появления Runtime Events
До настоящего момента Runtime умел описывать:
- транспортные интерфейсы;
- транспортные сообщения;
- намерения системы.
Однако отсутствовал механизм описания результатов выполнения этих намерений.
Например.
После выполнения
ConnectCommand
может произойти:
ConnectedEvent
или
ConnectFailedEvent
Это два разных факта.
Команда выражает желание выполнить действие.
Событие фиксирует результат выполнения этого действия.
Архитектурная идея
Build 059.12 завершает построение базовой event-driven модели Runtime.
Получается следующая архитектура.
Runtime
Commands
│
▼
Session
│
▼
Transport
│
▼
Events
Commands направляются вниз.
Events распространяются вверх.
Именно такое разделение используется в большинстве современных event-driven систем.
Новый модуль
Создан файл
src/market_data/acquisition/runtime/runtime_events.py
В модуле определены immutable-модели Runtime Events.
Использование Transport Messages
Транспортные события используют модели Build 059.10.
TransportTextMessage
TransportBinaryMessage
Runtime Event не знает содержимого сообщения.
Он лишь фиксирует факт передачи или получения транспортного payload.
TransportMessage
Для удобства типизации используется локальный alias.
TransportMessage =
TransportTextMessage
| TransportBinaryMessage
Он используется только внутри Runtime Events.
Никакой новой модели данных не создаётся.
Новые события
Build вводит девять Runtime Events.
ConnectedEvent
Фиксирует успешное открытие транспортного соединения.
Событие является маркерным.
Не содержит дополнительных данных.
Причина.
На текущем этапе Runtime отсутствует модель идентификатора соединения.
Добавлять подобные поля преждевременно.
DisconnectedEvent
Фиксирует завершение транспортного соединения.
Событие также является маркерным.
Причина отключения намеренно отсутствует.
Определение причин разрыва соединения относится к будущему уровню Reliability.
ConnectFailedEvent
Фиксирует неудачную попытку подключения.
Содержит:
reason
Причина хранится в виде строки.
Почему используется строка
Во время проектирования рассматривались варианты хранения:
- Exception;
- traceback;
- transport-specific error;
- websocket exception.
От данных вариантов было принято решение отказаться.
Runtime Event не должен зависеть от конкретной реализации транспорта.
Строковое описание полностью соответствует принципу transport-agnostic Runtime.
MessageReceivedEvent
Фиксирует получение транспортного сообщения.
Содержит:
TransportMessage
Runtime Event не анализирует payload.
Его обработкой занимаются последующие уровни Acquisition.
MessageSentEvent
Фиксирует успешную передачу транспортного сообщения.
Содержит:
TransportMessage
Это позволяет журналировать транспортный обмен, не анализируя содержимое сообщений.
ReconnectStartedEvent
Фиксирует начало новой попытки восстановления соединения.
Содержит:
attempt
Почему хранится номер попытки
Номер попытки является устойчивым Runtime-фактом.
Он потребуется:
- Supervisor;
- журналированию;
- диагностике;
- мониторингу Runtime.
ReconnectCompletedEvent
Фиксирует успешное восстановление соединения.
Также содержит:
attempt
что позволяет определить, на какой попытке произошло восстановление.
ReconnectFailedEvent
Фиксирует завершение очередной попытки восстановления соединения с ошибкой.
Содержит:
attempt
reason
Таким образом Runtime может описывать процесс восстановления без зависимости от конкретной реализации транспорта.
HeartbeatTimeoutEvent
Фиксирует превышение допустимого интервала heartbeat.
Содержит:
timeout_seconds
Хранится только настроенный порог ожидания.
Почему отсутствуют timestamp
Во время проектирования обсуждалось хранение:
timestamp
occurred_at
created_at
Было принято решение отказаться.
Причины.
Причина №1
В Runtime ещё отсутствует единая модель времени.
Причина №2
В разных окружениях время может поступать из различных источников.
Например:
- системные часы;
- монотонные часы;
- серверное время биржи.
До появления общей модели времени вводить timestamp преждевременно.
Почему отсутствует RuntimeEvent
Также обсуждалось создание общего базового класса.
Например.
RuntimeEvent
или
BaseEvent
От идеи отказались.
Причины.
Причина №1
Все события являются immutable dataclass.
Общего поведения между ними нет.
Причина №2
Преждевременная иерархия только усложняет архитектуру.
Причина №3
Общий базовый тип можно добавить позднее без нарушения обратной совместимости.
Почему отсутствует RuntimeEventType
Рассматривалось использование enum.
Например.
CONNECTED
DISCONNECTED
MESSAGE_RECEIVED
Было принято решение отказаться.
Тип события полностью определяется его классом.
Дополнительный enum создавал бы дублирование информации.
Почему отсутствуют Subscription Events
Изначально предполагалось добавить:
- SubscriptionRegisteredEvent;
- SubscriptionRemovedEvent;
- SubscriptionRestoredEvent.
После анализа архитектуры было принято решение перенести их в следующий Build.
Причина.
В Build 059.12 Subscription Manager ещё отсутствует.
Следовательно, пока отсутствует компонент, способный генерировать подобные события.
События должны появляться одновременно с соответствующим уровнем архитектуры.
Поэтому Subscription Events перенесены в Build 059.13.
Что НЕ входит в Build
Сознательно не реализованы:
- Event Bus;
- Dispatcher;
- Observer;
- callbacks;
- asyncio;
- Publisher;
- Subscriber;
- Session;
- Subscription Manager;
- обработчики событий;
- очередь событий.
Build содержит исключительно immutable-модели Runtime Events.
Обновлённая архитектура Runtime
После завершения Build Runtime приобретает следующий вид.
Runtime
Protocols
│
Transport Messages
│
Runtime Commands
│
Runtime Events
│
Transport
│
Network
Каждый уровень отвечает только за собственную область ответственности.
Следующие Build
Build 059.13
Subscription Manager.
Появятся:
- управление подписками;
- хранение Runtime-состояния;
- восстановление подписок;
- SubscriptionRegisteredEvent;
- SubscriptionRemovedEvent;
- SubscriptionRestoredEvent.
Build 059.14
WebSocket Session.
Появится координация:
- Commands;
- Events;
- Transport;
- Subscription Manager.
Build 059.15
Runtime Reliability.
Будут реализованы:
- Heartbeat;
- Reconnect;
- Supervisor;
- Scheduler;
- Ping/Pong.
Проверка Build
Выполнена полная проверка.
Компиляция
python -m compileall
Успешно.
Unit Tests
Созданы тесты:
test_runtime_events.py
Проверяется:
- создание каждого события;
- корректность хранения данных;
- поддержка TransportMessage;
- immutable-поведение dataclass.
Все тесты успешно пройдены.
Runtime Regression
Совместно проверены:
- Runtime Protocols;
- Runtime Transport Messages;
- Runtime Commands;
- Runtime Events.
Все Runtime-тесты успешно завершены.
Проверка репозитория
Выполнен
git diff --check
Ошибок форматирования не обнаружено.
Итог
Build 059.12 завершает формирование слоя Runtime Events.
Теперь Runtime имеет четыре полностью независимых уровня.
Protocols
│
Transport Messages
│
Runtime Commands
│
Runtime Events
Каждый уровень описывает собственную область ответственности.
Commands выражают намерения системы.
Events фиксируют уже произошедшие факты.
Подобное разделение делает архитектуру Runtime предсказуемой, расширяемой и соответствует общепринятым принципам построения event-driven систем.
Следующий Build посвящён созданию Subscription Manager, который станет первым Runtime-компонентом, использующим одновременно Commands и Events, сохраняя при этом независимость транспортного уровня от бизнес-логики Acquisition.