Files
dzentra_bot/docs/migrations/build_060_26_architecture.md

640 lines
26 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 уже включает:
```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.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
Добавлены:
```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.0060.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 завершён.