Files
dzentra_bot/docs/migrations/build_060_26_architecture.md

26 KiB
Raw Blame History

Build 060.26 — Integration and Regression Architecture

Статус: Accepted

Build: 060.26

Подсистема: Market Data Acquisition / Trade Stream Runtime

Дата начала: 2026-07-31

Дата завершения: 2026-07-31

Версия документа: 1.0


1. Назначение

Документ фиксирует архитектуру интеграционной проверки Production Trade Stream Runtime после завершения Build 060.25.

Build 060.25 доказал корректность production composition и lifecycle на детерминированных in-process boundaries. Build 060.26 проверяет тот же production graph через настоящие сетевые transport границы:

  • локальное WebSocket-соединение;
  • локальный HTTP endpoint Trade Recovery;
  • управляемые разрывы, задержки и ошибки сети;
  • многократные reconnect/recovery generations;
  • отсутствие утечек задач и ресурсов;
  • отдельные stress, soak и live exchange проверки.

Build является verification-first этапом. Production-код не меняется, если интеграционный сценарий не обнаружит реальный дефект.


2. Статус подэтапов

Подэтап Название Статус
060.26.0 Verification Contract and Test Taxonomy Accepted
060.26.1 Loopback Network Harness Accepted
060.26.2 Reconnect and Recovery Integration Accepted
060.26.3 Fault, Cancellation and Resource Verification Accepted
060.26.4 Stress and Soak Verification Accepted
060.26.5 Opt-in Dzengi Live Verification Accepted
060.26.6 Final Regression and Acceptance Accepted

Подэтапы 060.26.0060.26.3 были реализованы первой группой и прошли отдельный read-only review до начала длительных stress, soak и live exchange проверок.


3. Исходное состояние

Production Runtime уже включает:

Application bootstrap
        │
        ▼
TradeStreamProductionRuntime
        │
        ├── DzengiWebSocketTransport
        ├── WebSocketSession
        ├── WebSocketSubscriptionManager
        ├── RuntimeReconnectRecoveryCoordinator
        ├── RuntimeSupervisor
        ├── RuntimeScheduler
        └── TradeStreamStateStore
                    ▲
                    │
             Live + Recovery

Build 060.25.8 проверяет этот граф через управляемые fake WebSocket и REST зависимости. Такие тесты подтверждают ordering, lifecycle и cancellation, но не проверяют:

  • реальный WebSocket handshake и frame exchange;
  • поведение при TCP close/reset;
  • настоящий HTTP request из ExchangeRestClient;
  • взаимодействие asyncio с блокирующим REST worker;
  • освобождение реальных сокетов и серверных handler tasks.

Существующие диагностические app/scripts/check_* обращаются к протоколам Dzengi напрямую и не являются проверкой Production Runtime.


4. Классификация тестов

4.1. Unit

Unit suite:

  • не использует сетевые соединения;
  • выполняется обычной командой pytest;
  • остаётся основной быстрой регрессией проекта;
  • сохраняет существующие deterministic fake boundaries.

4.2. Integration

Маркер:

integration

Integration suite:

  • запускает настоящие локальные TCP/WebSocket/HTTP соединения;
  • использует production factory и production transport/client;
  • не обращается к интернету;
  • управляет задержками и ошибками через локальный test harness;
  • запускается отдельной явной командой.

4.3. Stress

Маркер:

stress

Stress suite:

  • использует только локальные endpoints;
  • проверяет большие последовательности Trade и reconnect generations;
  • не входит в обычную unit или integration regression;
  • не задаёт хрупких требований к абсолютной производительности.

4.4. Live

Маркер:

live

Live suite:

  • запускается только при явном opt-in;
  • требует явно заданные WebSocket URL, REST URL и symbols;
  • не использует fallback;
  • выполняет только публичные market-data операции;
  • не создаёт, не изменяет и не отменяет торговые ордера;
  • не входит в обычную регрессию.

4.5. Команды

# Обычная unit-регрессия
.venv/bin/python -m pytest -q

# Локальная интеграция
.venv/bin/python -m pytest -q -m integration tests/integration

# Локальный stress/soak
.venv/bin/python -m pytest -q -m stress tests/stress

# Расширенный 15-минутный soak
DZENTRA_SOAK_SECONDS=900 \
.venv/bin/python -m pytest -q -m stress -k soak tests/stress

# Явная live exchange проверка
DZENTRA_RUN_LIVE_TESTS=1 \
DZENTRA_LIVE_REST_URL=https://api-adapter.dzengi.com \
DZENTRA_LIVE_WS_URL=wss://api-adapter.dzengi.com/connect \
DZENTRA_LIVE_SYMBOLS=BTC/USD_LEVERAGE \
.venv/bin/python -m pytest -q -m live tests/live

Конфигурация pytest по умолчанию исключает integration, stress и live. Поэтому добавление сетевых сценариев не меняет контракт обычного unit suite.


5. Loopback Network Harness

Test harness владеет только тестовой инфраструктурой:

Production factory
        │
        ▼
TradeStreamProductionRuntime
        │
        ├── DzengiWebSocketTransport
        │             │
        │             ▼
        │     Loopback WebSocket server
        │
        └── ExchangeRestClient
                      │
                      ▼
              Loopback HTTP server

Дополнительный TCP fault proxy может:

  • пропускать трафик без изменений;
  • обрывать конкретное соединение;
  • прекращать передачу client → server frames;
  • моделировать отсутствие Pong без изменения production transport.

Harness записывает:

  • номера TCP/WebSocket connections;
  • порядок subscription requests;
  • correlation ID;
  • порядок HTTP recovery requests;
  • query parameters recovery window;
  • открытые server handlers и relay tasks.

Harness не:

  • подменяет production parsing или consistency;
  • вызывает Recovery Controller напрямую;
  • создаёт собственный checkpoint;
  • меняет production retry policy;
  • хранит market state вместо Runtime.

6. Основной reconnect/recovery сценарий

Обязательная последовательность:

connect
  → subscribe
  → ACK
  → live Trade
  → checkpoint
  → network disconnect
  → reconnect
  → restore subscription
  → REST Recovery started
  → buffered live Trade received
  → recovered Trade accepted
  → buffered live Trade accepted

Проверяемые инварианты:

  1. новый connection создаётся один раз для одной generation;
  2. subscription restore происходит раньше первого REST request;
  3. Live processing закрыт на время Recovery;
  4. recovered Trade обрабатывается раньше buffered live Trade;
  5. общий checkpoint принадлежит одному TradeStreamStateStore;
  6. дубликаты на границе Recovery не меняют результат;
  7. после успешного Recovery receive loop продолжает работу.

7. Fault and Cancellation Matrix

Первая группа Build проверяет:

  • graceful и abrupt WebSocket disconnect;
  • повторные успешные reconnect generations;
  • одновременное наблюдение одной ошибки receive loop и Heartbeat;
  • Ping/Pong timeout через управляемый TCP blackhole;
  • HTTP latency;
  • HTTP error;
  • невалидный WebSocket JSON;
  • остановку Runtime во время активного REST worker;
  • terminal error без обработки buffered live Trade;
  • освобождение Runtime tasks, WebSocket handlers, relay tasks и HTTP server thread.

Каждый сценарий имеет внешний timeout. Ожидание состояния выполняется по наблюдаемому событию или условию, а не через фиксированную длинную паузу.


8. Error Policy

Build 060.26 проверяет принятую политику, но не меняет её неявно:

  • startup failure включённого Trade Stream является terminal;
  • неуспешная reconnect/recovery операция является terminal;
  • повторные успешные reconnect возможны в следующих generations;
  • один transport failure не создаёт конкурирующие reconnect;
  • cancellation не открывает Live gate до завершения REST worker;
  • cleanup error не скрывает primary Runtime error.

Если тест обнаруживает несоответствие production-кода этим правилам, дефект сначала документируется и проходит отдельное согласование.


9. Resource Ownership

Production Runtime продолжает владеть:

  • startup task;
  • receive task;
  • Scheduler task;
  • Recovery worker task;
  • WebSocket Session lifecycle.

Test harness владеет:

  • WebSocket listening socket;
  • WebSocket server handler tasks;
  • HTTP listening socket;
  • HTTP server thread;
  • TCP proxy listening socket;
  • TCP relay tasks и stream writers.

После каждого теста должны быть закрыты обе группы ресурсов. Проверка не считается завершённой, если остались:

  • именованные Runtime tasks;
  • активные server handlers;
  • незавершённые relay tasks;
  • незакрытые stream writers;
  • живой HTTP server thread.

10. Границы Build

Build 060.26 не реализует:

  • persistent Trade, Quote или Candle storage;
  • persistent checkpoint;
  • startup recovery после перезапуска процесса;
  • Historical Queries;
  • Replay API;
  • Analytics API;
  • новый reconnect retry/backoff policy;
  • автоматическое включение live exchange tests.

Эти задачи относятся к Build 060.27060.29 либо требуют отдельного архитектурного решения.


11. Документационная политика

Во время Build 060.26:

  • этот документ хранит архитектуру и фактический статус подэтапов;
  • итоговый результат зафиксирован в build_060_26.md;
  • в master-roadmap.md меняется только фактический статус Build;
  • полная ревизия исторических документов и старой нумерации откладывается до Build 060.30.

12. Критерии завершения 060.26.0060.26.3

  • pytest test taxonomy отделяет network suites от unit regression;
  • локальный WebSocket server работает с production transport;
  • локальный HTTP server работает с production ExchangeRestClient;
  • reconnect восстанавливает подписку до Recovery;
  • Recovery завершается до обработки buffered live Trade;
  • Ping/Pong и network disconnect проходят single-flight coordination;
  • HTTP error и invalid message завершают Runtime предсказуемо;
  • cancellation дожидается REST worker;
  • все тестовые и production ресурсы освобождаются;
  • целевой integration suite и полная unit-регрессия проходят;
  • отдельный read-only review не содержит незакрытых findings.

13. Фактическая реализация 060.26.0060.26.3

Добавлены:

app/tests/integration/market_data/acquisition/runtime/
    loopback_trade_exchange.py
    test_trade_stream_loopback_integration.py

Изменены только для классификации и фактического статуса:

app/pytest.ini
docs/roadmap/master-roadmap.md

Loopback harness содержит:

  • настоящий WebSocket server;
  • настоящий HTTP server в отдельном управляемом thread;
  • TCP fault proxy с client → server blackhole;
  • запись connection, subscription и REST request order;
  • явный cleanup всех принадлежащих harness ресурсов.

Production-код Build 060.25 не изменён.

Реализованы десять integration-сценариев:

  1. production WebSocket transport обновляет общий checkpoint;
  2. subscription restore предшествует REST Recovery, а recovered Trade предшествует buffered live Trade;
  3. graceful и abrupt disconnect проходят три последовательных reconnect generations;
  4. HTTP Recovery error блокирует buffered live и завершает Runtime;
  5. невалидный WebSocket JSON является terminal error;
  6. cancellation ждёт завершения реального HTTP worker;
  7. Ping timeout и receive failure используют одну reconnect generation, при этом явно подтверждено участие Scheduler и receive loop;
  8. timeout во время Runtime startup останавливает созданную Runtime task;
  9. ошибка запуска WebSocket server не оставляет запущенным HTTP server;
  10. ошибка cleanup WebSocket server не мешает последующему cleanup HTTP server.

Проверки на 2026-07-31:

Integration target:                        10 passed
Integration repeated three times:          30 passed
Integration with ResourceWarning as error: 10 passed
Full unit regression:                    1869 passed
Default-suite integration deselection:     10 deselected

Первичный review выявил четыре замечания в test harness: владение Runtime task при неуспешном startup, ограниченность и exception-safety cleanup, две ошибки статической типизации и недостаточно явное доказательство участия обоих single-flight callers. Все замечания исправлены и закрыты regression-тестами.

Повторный read-only review не выявил новых findings. Подэтапы 060.26.0060.26.3 имеют статус Accepted.


14. Фактическая реализация 060.26.4

Добавлены:

app/tests/support/
    __init__.py
    trade_stream_runtime.py

app/tests/stress/market_data/acquisition/runtime/
    test_trade_stream_runtime_stress.py

Общий test-support владеет только test-side запуском и остановкой production Runtime. Integration и stress tests используют один lifecycle contract и не импортируют функции из других test_*.py.

Loopback harness дополнен наблюдаемыми счётчиками открытых WebSocket connections и TCP stream writers. Production-код не изменён.

Реализованы четыре локальных stress/soak-сценария:

  1. отдельная проверка подтверждает, что tracemalloc filter наблюдает ненулевые allocations подсистемы;
  2. 20 000 последовательных Trades проверяют ordering, итоговый checkpoint, ограничение deduplication window до 10 000 записей и остаточный рост памяти после прогрева не более 4 MiB;
  3. 30 reconnect generations чередуют graceful close, TCP abort и Ping timeout и проверяют точное соответствие generations, connections, subscriptions и REST Recovery requests;
  4. стандартный 120-секундный soak обрабатывает 6 000 Trades и 24 reconnect в одном Runtime lifecycle. Через DZENTRA_SOAK_SECONDS=900 доступен отдельный 15-минутный профиль.

После каждого сценария проверяется отсутствие именованных Runtime tasks, активных WebSocket/TCP handlers, relay tasks, tracked stream writers и живого HTTP server thread. Между reconnect проверяются состояние Runtime, Live gate и отсутствие незавершённой Recovery task.

Проверки на 2026-07-31:

Fixed stress target:                       3 passed in 6.75s
Standard soak with ResourceWarning error:  1 passed in 120.09s
Integration regression:                   11 passed
Full unit regression:                      1869 passed
Default-suite network deselection:         15 deselected

Первичный review выявил три замечания: writer tracking очищался до доказательства закрытия transport, memory filter мог пройти с пустым baseline, а stress ожидания неявно наследовали integration timeout. Все замечания исправлены и закрыты regression-проверками.

Повторный read-only review не выявил новых findings. Подэтап 060.26.4 имеет статус Accepted.


15. Фактическая реализация 060.26.5

Добавлены:

app/src/market_data/acquisition/
    trade_id_sequence.py

app/tests/support/
    async_wait.py
    live_trade_stream.py

app/tests/live/market_data/acquisition/runtime/
    test_trade_stream_runtime_live.py

app/tests/unit/
    test_live_trade_stream_support.py
    test_trade_stream_runtime_support.py

app/tests/unit/market_data/acquisition/
    test_trade_id_sequence.py

Live configuration contract требует:

  • точный opt-in DZENTRA_RUN_LIVE_TESTS=1;
  • отдельный HTTPS REST base URL;
  • отдельный WSS URL с endpoint /connect;
  • ровно один явно заданный symbol;
  • отсутствие embedded credentials, query и fragment в URL.

Без opt-in live test завершается SKIP до импорта production configuration и до создания сетевых компонентов. При включённом opt-in неполная или небезопасная конфигурация является ошибкой, а не получает fallback из .env, DEFAULT_SYMBOL или demo settings.

Test-side Settings создаются явно и всегда содержат пустые API key и secret. Telegram, application bootstrap, торговые endpoints и order operations не используются.

Ожидание рыночного события ограничено 600 секундами. Runtime failure при этом распространяется немедленно через fail-fast helper и не маскируется длительным ожиданием внешнего рынка.

Реализован один ограниченный live-сценарий:

production factory
  → Runtime RUNNING
  → первая Live Trade
  → checkpoint
  → один RuntimeReconnectRecoveryCoordinator.reconnect()
  → subscription restore
  → production REST Recovery
  → новая Live Trade после Recovery
  → graceful stop
  → отсутствие owned Runtime tasks

Сценарий намеренно использует текущий production Recovery endpoint /api/v1/aggTrades. Исторические исследования и диагностический скрипт проекта используют /api/v2/aggTrades. Фактическая проверка подтвердила HTTP 200 и одинаковую структуру ответа обоих endpoint для BTC/USD_LEVERAGE; production endpoint менять не требуется.

Первый live-запуск выявил, что Dzengi возвращает signed 32-bit Trade ID, включая отрицательные значения. Следующий запуск выявил закономерный повтор одной сделки через WebSocket и REST с разным transport source. Оба факта были реальными production-дефектами прежнего контракта и исправлены:

  • WebSocket и REST validation принимают полный signed 32-bit диапазон;
  • одинаковый market fact дедуплицируется независимо от transport source, но различие цены, количества, времени или стороны остаётся consistency error;
  • единый trade_id_sequence задаёт modular-порядок по циклу 2^32;
  • переходы INT32_MAX → INT32_MIN и -1 → 0 считаются продвижением;
  • Consistency, Recovery normalizer и live assertions используют один rollover-aware contract;
  • расстояние ровно в половину цикла отклоняется как неоднозначное;
  • recovery-набор должен занимать меньше половины цикла, что существенно шире максимального часового Runtime Recovery window.

Итоговые проверки на 2026-07-31:

Rollover/Recovery target:                 401 passed
Integration regression:                   11 passed
Fixed stress regression:                   3 passed
Full offline regression:                1930 passed
Default-suite network deselection:        16 deselected
Opt-in Dzengi live verification:           1 passed in 140.42s

Финальный read-only review проверил modular ordering, signed boundary, cross-source identity, fail-fast ownership, bounded timeout и отсутствие обычных сравнений Trade ID в production/live ordering paths. Новых findings не обнаружено. Подэтап 060.26.5 имеет статус Accepted.


16. Фактическая реализация 060.26.6

Финальная приёмка повторно проверила все уровни Build без изменения production-кода:

  1. десять последовательных запусков локального integration-набора;
  2. fixed stress на 20 000 Trades и 30 reconnect generations;
  3. стандартный 120-секундный soak на 6 000 Trades и 24 reconnect;
  4. расширенный 900-секундный soak на 45 000 Trades и 180 reconnect;
  5. полную offline-регрессию проекта;
  6. итоговый read-only review production, test и documentation diff.

Во всех сетевых и нагрузочных сценариях ResourceWarning считался ошибкой. После завершения подтверждено отсутствие принадлежащих Runtime задач, WebSocket/TCP handlers, relay tasks, tracked stream writers и живого HTTP server thread.

Финальные результаты на 2026-07-31:

Integration repeated ten times:          110 passed
Fixed stress target:                       3 passed, 1 deselected in 6.81s
Standard 120-second soak:                  1 passed, 3 deselected in 120.09s
Extended 900-second soak:                  1 passed, 3 deselected in 900.11s
Full offline regression:                1930 passed, 16 deselected in 3.64s
Opt-in Dzengi live verification:           1 passed in 140.42s
git diff --check:                           clean

Live verification не запускалась повторно в 060.26.6: её успешный результат получен после последних production-изменений signed Trade ID, а сам acceptance-этап production-код больше не менял.

Итоговый review не выявил новых findings. Production-изменения Build ограничены реальными дефектами signed Trade ID, обнаруженными live verification. Изменение .gitignore не относится к Build 060.26 и не должно включаться в его staging.

Подэтап 060.26.6 принят. Build 060.26 завершён.