Build 060.29: implement Market Data Access and Replay
This commit is contained in:
236
docs/migrations/build_060_29.md
Normal file
236
docs/migrations/build_060_29.md
Normal file
@@ -0,0 +1,236 @@
|
||||
# Build 060.29 — Market Data Access and Replay
|
||||
|
||||
**Engineering Migration Report**
|
||||
|
||||
---
|
||||
|
||||
## Контроль документа
|
||||
|
||||
| Свойство | Значение |
|
||||
|---|---|
|
||||
| Build | 060.29 |
|
||||
| Статус | Completed |
|
||||
| Подсистема | Market Data / Historical Access and Replay |
|
||||
| Компонент | Canonical History and Deterministic Replay |
|
||||
| Дата завершения | 2026-08-02 |
|
||||
| Версия | 1.0 |
|
||||
|
||||
---
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- `build_060_29_architecture.md` — архитектура, решения и подробные
|
||||
результаты подэтапов 060.29.0–060.29.8;
|
||||
- `build_060_28.md` — Persistent Checkpoint and Startup Recovery;
|
||||
- `dzentra_target_architecture.md` — целевая архитектура Dzentra;
|
||||
- `master-roadmap.md` — дальнейшая последовательность Build.
|
||||
|
||||
---
|
||||
|
||||
## 1. Назначение Build
|
||||
|
||||
Build 060.29 добавил безопасное чтение сохранённых Canonical Market
|
||||
Data и их детерминированное воспроизведение.
|
||||
|
||||
Итоговая цепочка:
|
||||
|
||||
```text
|
||||
PostgreSQL Canonical Market Data
|
||||
↓
|
||||
Historical Access
|
||||
↓
|
||||
bounded immutable ReplayPlan
|
||||
↓
|
||||
ReplaySession + Deterministic Clock
|
||||
↓
|
||||
явно переданный Canonical consumer
|
||||
```
|
||||
|
||||
Historical Access и Replay являются read-side подсистемами. Они не
|
||||
заменяют Acquisition Runtime, не продвигают operational checkpoint и не
|
||||
записывают воспроизводимые события обратно в Market Data Storage.
|
||||
|
||||
---
|
||||
|
||||
## 2. Завершённые подэтапы
|
||||
|
||||
| Подэтап | Название | Статус |
|
||||
|---|---|---|
|
||||
| 060.29.0 | Architecture, Boundaries and Ordering Policy | Accepted |
|
||||
| 060.29.1 | Historical Access and Replay Contracts | Accepted |
|
||||
| 060.29.2 | Global Replay Sequence Migration and PostgreSQL Trade History | Accepted |
|
||||
| 060.29.3 | Quote/Candle History and Snapshot Plan Builder | Accepted |
|
||||
| 060.29.4 | Deterministic Replay Clock | Accepted |
|
||||
| 060.29.5 | Replay Session and Engine | Accepted |
|
||||
| 060.29.6 | Consumer and Composition Integration | Accepted |
|
||||
| 060.29.7 | PostgreSQL Replay and Failure Verification | Accepted |
|
||||
| 060.29.8 | Final Regression and Acceptance | Accepted |
|
||||
|
||||
Каждый подэтап проходил отдельный read-only review. Findings
|
||||
исправлялись и закрывались regression-тестами до принятия.
|
||||
|
||||
---
|
||||
|
||||
## 3. Реализованная архитектура
|
||||
|
||||
### 3.1. Глобальная последовательность Replay
|
||||
|
||||
Migration 9 добавила один shared PostgreSQL sequence для Trades, Quotes
|
||||
и Candle revisions. Неизменяемый `replay_sequence` задаёт общий
|
||||
детерминированный порядок для событий с одинаковым временем и
|
||||
сохраняется при duplicate или provenance update.
|
||||
|
||||
Существующие writers совместимы с sequence для одиночных, пакетных и
|
||||
конкурентных записей. Default, существующие месячные и будущие partitions
|
||||
получают одинаковые constraints, trigger и keyset indexes.
|
||||
|
||||
Migration выполняется атомарно и использует единый порядок блокировок с
|
||||
Partition Manager и writers.
|
||||
|
||||
### 3.2. Historical Access
|
||||
|
||||
DB-neutral read-контракты отделены от write-only Storage API. Отдельные
|
||||
PostgreSQL readers возвращают типизированные неизменяемые страницы:
|
||||
|
||||
- Trades — по `executed_at`;
|
||||
- Quotes — по `received_at`;
|
||||
- Candle revisions — по `open_time`.
|
||||
|
||||
Запросы используют полуоткрытый временной диапазон и forward keyset
|
||||
pagination. Cursor связан с точным query scope. Строки PostgreSQL строго
|
||||
проверяются перед созданием Canonical `Trade`, `Quote` или `Candle`.
|
||||
|
||||
### 3.3. Bounded Replay snapshot
|
||||
|
||||
Replay Plan Builder выполняет один статический snapshot-запрос в
|
||||
короткой транзакции `READ ONLY REPEATABLE READ`. Все выбранные события
|
||||
материализуются до возврата `ReplayPlan`; cursor, transaction и
|
||||
connection освобождаются до первого вызова consumer.
|
||||
|
||||
Порядок Replay равен `(replay_at, replay_sequence)`. Для Candle временем
|
||||
Replay является `observed_at`, поэтому ревизия не появляется раньше
|
||||
момента, когда она стала известна системе. Превышение `max_records`
|
||||
завершается явной ошибкой без частичного plan.
|
||||
|
||||
### 3.4. Clock, Session и Composition
|
||||
|
||||
`DeterministicReplayClock` работает только с aware UTC-временем,
|
||||
разрешает равное время и запрещает движение назад. Он не читает wall
|
||||
clock и не имеет reset.
|
||||
|
||||
`ReplaySession` является необратимой one-shot сущностью. Она
|
||||
последовательно продвигает Clock и ожидает один consumer-вызов для
|
||||
каждого события. Ошибки и cancellation сохраняют явное terminal state и
|
||||
не скрываются.
|
||||
|
||||
`ReplaySessionFactory` синхронно подготавливает новый граф
|
||||
`Plan → Clock → consumer → Session` для каждого вызова. Default consumer,
|
||||
автоматический startup, скрытая задача и отдельный `ReplayEngine` не
|
||||
добавлены. Caller отдельно владеет `session.run()`.
|
||||
|
||||
### 3.5. Статическая проверка типов
|
||||
|
||||
Pyright закреплён как обязательный gate в режиме `standard`, совпадающем
|
||||
с Pylance проекта. Канонический запуск — `scripts/check_python_types.sh`;
|
||||
допустимый результат — только `0 errors, 0 warnings`. Тот же gate входит
|
||||
в обычную offline pytest-регрессию.
|
||||
|
||||
---
|
||||
|
||||
## 4. Real PostgreSQL verification
|
||||
|
||||
Безопасный opt-in harness требует отдельную локальную базу с именем
|
||||
`dzentra_test_*`, явный флаг и отдельный DSN.
|
||||
|
||||
Настоящий PostgreSQL 16 подтвердил:
|
||||
|
||||
- атомарный детерминированный backfill migration 9;
|
||||
- общий sequence и совместимость всех writers/partitions;
|
||||
- keyset pagination Trades, Quotes и Candle revisions;
|
||||
- единый mixed-type Replay order;
|
||||
- устойчивый `REPEATABLE READ` snapshot при concurrent commit;
|
||||
- освобождение PostgreSQL resources до playback;
|
||||
- limit, integrity, backend, consumer и cancellation paths;
|
||||
- независимые графы двух одновременно готовящихся Replay Session;
|
||||
- полный путь `Loopback Runtime → Storage → History → Replay`;
|
||||
- отсутствие изменения durable данных и operational checkpoint.
|
||||
|
||||
---
|
||||
|
||||
## 5. Финальные результаты
|
||||
|
||||
Итоговая приёмка выполнена 2026-08-02:
|
||||
|
||||
```text
|
||||
Pyright mandatory gate: 0 errors, 0 warnings
|
||||
Pyright pytest gate: 1 passed
|
||||
Expanded Access/Replay/Storage unit: 545 passed
|
||||
PostgreSQL Replay target repeated: 10 × 12 passed
|
||||
Full PostgreSQL Storage integration: 77 passed
|
||||
Full integration with PostgreSQL: 90 passed
|
||||
PostgreSQL suite without opt-in: 77 skipped
|
||||
Fixed stress target: 3 passed, 1 deselected
|
||||
Full offline regression: 2681 passed, 95 deselected
|
||||
pip check: clean
|
||||
Compileall через временный pycache: clean
|
||||
Tracked/untracked whitespace checks: clean
|
||||
Три независимых read-only review: clean
|
||||
```
|
||||
|
||||
В integration и stress наборах `ResourceWarning` считался ошибкой.
|
||||
Production-код на финальном подэтапе 060.29.8 не изменялся: матрица не
|
||||
выявила реального дефекта.
|
||||
|
||||
---
|
||||
|
||||
## 6. Эксплуатационное предупреждение migration 9
|
||||
|
||||
Migration 9 является блокирующей. Её длительность пропорциональна уже
|
||||
накопленному объёму Market Data, потому что существующие строки получают
|
||||
глобальный `replay_sequence`, после чего создаются constraints и
|
||||
индексы.
|
||||
|
||||
Для текущей небольшой базы выбранный атомарный вариант разумен. Перед
|
||||
применением к большой production-базе обязательны резервная копия,
|
||||
замер длительности на сопоставимом объёме и отдельное maintenance
|
||||
window. Online staged migration в Build 060.29 не реализована и требует
|
||||
отдельного архитектурного решения.
|
||||
|
||||
---
|
||||
|
||||
## 7. Границы Build
|
||||
|
||||
Build 060.29 намеренно не реализует:
|
||||
|
||||
- initial historical backfill с биржи;
|
||||
- гарантию полной биржевой истории без completeness metadata;
|
||||
- точное воспроизведение порядка исходных сетевых пакетов;
|
||||
- production consumers для persistent Quotes и Candles;
|
||||
- автоматический запуск Replay из Bootstrap;
|
||||
- default/no-op Replay consumer;
|
||||
- Backtesting, Analytics API или торговую симуляцию;
|
||||
- online migration 9 для большой production-базы.
|
||||
|
||||
Каждая Historical page отражает committed-состояние на время своего
|
||||
запроса. Последовательная pagination охватывает все фактически
|
||||
сохранённые строки диапазона только при неизменном dataset между
|
||||
страницами; единого межстраничного snapshot нет. Период доступной истории
|
||||
определяется моментом включения persistent storage и Retention Policy, а
|
||||
не самим Replay API.
|
||||
|
||||
Постороннее пользовательское изменение `.gitignore` не относится к
|
||||
Build 060.29 и не должно включаться в его staging.
|
||||
|
||||
---
|
||||
|
||||
## 8. Итог
|
||||
|
||||
Build 060.29 завершён и принят.
|
||||
|
||||
Dzentra получила отдельный Historical Access, общий детерминированный
|
||||
порядок сохранённых Canonical Market Data и caller-owned Replay без
|
||||
скрытого lifecycle. Сохранённую рыночную историю теперь можно безопасно
|
||||
читать и воспроизводить одинаковыми Canonical объектами.
|
||||
|
||||
Следующий этап — Build 060.30, итоговый аудит и документальное закрытие
|
||||
ветки Trades Feed.
|
||||
Reference in New Issue
Block a user