# 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.0–060.26.3 были реализованы первой группой и прошли отдельный read-only review до начала длительных stress, soak и live exchange проверок. --- ## 3. Исходное состояние Production Runtime уже включает: ```text 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 Маркер: ```text integration ``` Integration suite: - запускает настоящие локальные TCP/WebSocket/HTTP соединения; - использует production factory и production transport/client; - не обращается к интернету; - управляет задержками и ошибками через локальный test harness; - запускается отдельной явной командой. ### 4.3. Stress Маркер: ```text stress ``` Stress suite: - использует только локальные endpoints; - проверяет большие последовательности Trade и reconnect generations; - не входит в обычную unit или integration regression; - не задаёт хрупких требований к абсолютной производительности. ### 4.4. Live Маркер: ```text live ``` Live suite: - запускается только при явном opt-in; - требует явно заданные WebSocket URL, REST URL и symbols; - не использует fallback; - выполняет только публичные market-data операции; - не создаёт, не изменяет и не отменяет торговые ордера; - не входит в обычную регрессию. ### 4.5. Команды ```text # Обычная 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 владеет только тестовой инфраструктурой: ```text 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 сценарий Обязательная последовательность: ```text 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.27–060.29 либо требуют отдельного архитектурного решения. --- ## 11. Документационная политика Во время Build 060.26: - этот документ хранит архитектуру и фактический статус подэтапов; - итоговый результат зафиксирован в `build_060_26.md`; - в `master-roadmap.md` меняется только фактический статус Build; - полная ревизия исторических документов и старой нумерации откладывается до Build 060.30. --- ## 12. Критерии завершения 060.26.0–060.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.0–060.26.3 Добавлены: ```text app/tests/integration/market_data/acquisition/runtime/ loopback_trade_exchange.py test_trade_stream_loopback_integration.py ``` Изменены только для классификации и фактического статуса: ```text 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: ```text 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.0–060.26.3 имеют статус `Accepted`. --- ## 14. Фактическая реализация 060.26.4 Добавлены: ```text 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: ```text 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 Добавлены: ```text 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-сценарий: ```text 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: ```text 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: ```text 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 завершён.