Files
dzentra_bot/docs/migrations/build_059_12.md

13 KiB
Raw Blame History

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.