26 KiB
Build 060.26 — Integration and Regression Architecture
Статус: Completed
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 уже включает:
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
Проверяемые инварианты:
- новый connection создаётся один раз для одной generation;
- subscription restore происходит раньше первого REST request;
- Live processing закрыт на время Recovery;
- recovered Trade обрабатывается раньше buffered live Trade;
- общий checkpoint принадлежит одному
TradeStreamStateStore; - дубликаты на границе Recovery не меняют результат;
- после успешного 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
Добавлены:
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-сценариев:
- production WebSocket transport обновляет общий checkpoint;
- subscription restore предшествует REST Recovery, а recovered Trade предшествует buffered live Trade;
- graceful и abrupt disconnect проходят три последовательных reconnect generations;
- HTTP Recovery error блокирует buffered live и завершает Runtime;
- невалидный WebSocket JSON является terminal error;
- cancellation ждёт завершения реального HTTP worker;
- Ping timeout и receive failure используют одну reconnect generation, при этом явно подтверждено участие Scheduler и receive loop;
- timeout во время Runtime startup останавливает созданную Runtime task;
- ошибка запуска WebSocket server не оставляет запущенным HTTP server;
- ошибка 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.0–060.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-сценария:
- отдельная проверка подтверждает, что
tracemallocfilter наблюдает ненулевые allocations подсистемы; - 20 000 последовательных Trades проверяют ordering, итоговый checkpoint, ограничение deduplication window до 10 000 записей и остаточный рост памяти после прогрева не более 4 MiB;
- 30 reconnect generations чередуют graceful close, TCP abort и Ping timeout и проверяют точное соответствие generations, connections, subscriptions и REST Recovery requests;
- стандартный 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-кода:
- десять последовательных запусков локального integration-набора;
- fixed stress на 20 000 Trades и 30 reconnect generations;
- стандартный 120-секундный soak на 6 000 Trades и 24 reconnect;
- расширенный 900-секундный soak на 45 000 Trades и 180 reconnect;
- полную offline-регрессию проекта;
- итоговый 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 завершён.