Build 060.27: implement Persistent Market Data Storage
This commit is contained in:
224
docs/migrations/build_060_27.md
Normal file
224
docs/migrations/build_060_27.md
Normal file
@@ -0,0 +1,224 @@
|
||||
# Build 060.27 — Persistent Market Data Storage
|
||||
|
||||
**Engineering Migration Report**
|
||||
|
||||
---
|
||||
|
||||
## Контроль документа
|
||||
|
||||
| Свойство | Значение |
|
||||
|---|---|
|
||||
| Build | 060.27 |
|
||||
| Статус | Completed |
|
||||
| Подсистема | Market Data Acquisition / Persistent Storage |
|
||||
| Компонент | Canonical Market Data Storage |
|
||||
| Дата завершения | 2026-08-01 |
|
||||
| Версия | 1.0 |
|
||||
|
||||
---
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- `build_060_27_architecture.md` — архитектура, решения и подробные
|
||||
результаты подэтапов 060.27.0–060.27.8;
|
||||
- `build_060_26.md` — итог Runtime Integration and Regression;
|
||||
- `dzentra_target_architecture.md` — место Market Data Storage в целевой
|
||||
архитектуре Dzentra;
|
||||
- `master-roadmap.md` — дальнейшая последовательность Build.
|
||||
|
||||
---
|
||||
|
||||
## 1. Назначение Build
|
||||
|
||||
Build 060.27 добавил долговременное PostgreSQL-хранилище Canonical
|
||||
Market Data. Production Trade Stream теперь может сохранять Trades
|
||||
независимо от времени жизни процесса, а Quotes и Candles получили
|
||||
готовые storage-контракты и repositories без подключения существующих
|
||||
consumers.
|
||||
|
||||
Главная гарантия интеграции:
|
||||
|
||||
```text
|
||||
validated Canonical Trade
|
||||
↓
|
||||
durable PostgreSQL write
|
||||
↓
|
||||
operational checkpoint advancement
|
||||
```
|
||||
|
||||
Ошибка включённого persistent storage является фатальной. Runtime не
|
||||
продвигает checkpoint и не продолжает работу так, будто Trade сохранён.
|
||||
|
||||
---
|
||||
|
||||
## 2. Завершённые подэтапы
|
||||
|
||||
| Подэтап | Название | Статус |
|
||||
|---|---|---|
|
||||
| 060.27.0 | Storage Contract and Boundaries | Accepted |
|
||||
| 060.27.1 | PostgreSQL Schema, Migrations and Pool | Accepted |
|
||||
| 060.27.2 | Persistent Trade Repository | Accepted |
|
||||
| 060.27.3 | Quote and Candle Repositories | Accepted |
|
||||
| 060.27.4 | Storage API and Retention | Accepted |
|
||||
| 060.27.5 | Trade Runtime Persistence Integration | Accepted |
|
||||
| 060.27.6 | Bootstrap and Lifecycle Integration | Accepted |
|
||||
| 060.27.7 | Integration and Failure Verification | Accepted |
|
||||
| 060.27.8 | Final Regression and Acceptance | Accepted |
|
||||
|
||||
Каждый подэтап проходил отдельный read-only review. Findings
|
||||
исправлялись и закрывались regression-тестами до принятия.
|
||||
|
||||
---
|
||||
|
||||
## 3. Реализованная архитектура
|
||||
|
||||
### 3.1. Schema, migrations и pool
|
||||
|
||||
Создана PostgreSQL schema `market_data` с partitioned таблицами:
|
||||
|
||||
- `trades` — Canonical Trades;
|
||||
- `quotes` — неизменяемые Canonical Quote snapshots;
|
||||
- `candle_revisions` — наблюдавшиеся ревизии Candles;
|
||||
- `partition_registry` — реестр Dzentra-managed partitions.
|
||||
|
||||
Семь упорядоченных миграций применяются в одной транзакции под
|
||||
transaction-level advisory lock. История хранится в
|
||||
`public.storage_schema_migrations`; неизвестная версия или другое имя
|
||||
миграции являются фатальным schema mismatch.
|
||||
|
||||
`PostgresConnectionPool` не выполняет I/O в конструкторе. Pool явно
|
||||
открывается Application lifecycle перед Runtime и закрывается только
|
||||
после полной остановки Trade Runtime.
|
||||
|
||||
### 3.2. Canonical repositories
|
||||
|
||||
Repository-контракты различают три результата:
|
||||
|
||||
- `INSERTED` — сохранено новое событие;
|
||||
- `DUPLICATE` — идентичное событие уже существует;
|
||||
- `PROVENANCE_UPDATED` — Canonical payload совпал, добавлен новый путь
|
||||
наблюдения.
|
||||
|
||||
Trade identity задаётся `venue + symbol + trade_id + executed_at`.
|
||||
Время защищает историю от коллизии после полного signed 32-bit rollover
|
||||
Trade ID. Разный transport source не создаёт Canonical conflict, а
|
||||
различие цены, количества, времени или стороны создаёт явную ошибку.
|
||||
|
||||
Quote identity использует `venue + symbol + received_at`. Candle
|
||||
identity использует `venue + symbol + interval + open_time +
|
||||
observed_at`, поэтому промежуточные и финальные ревизии не
|
||||
перезаписываются скрытно.
|
||||
|
||||
Одиночные и пакетные операции транзакционны. Конфликт или database
|
||||
error откатывает всю пакетную запись.
|
||||
|
||||
### 3.3. Partitions и retention
|
||||
|
||||
Каждая основная таблица имеет default partition. Управляемый partition
|
||||
manager под общим advisory lock:
|
||||
|
||||
1. проверяет фактическое состояние PostgreSQL и registry;
|
||||
2. создаёт месячную таблицу;
|
||||
3. переносит подходящие default rows;
|
||||
4. подключает partition в той же транзакции.
|
||||
|
||||
Retention по умолчанию выключен. Явный запуск удаляет только
|
||||
зарегистрированные истёкшие partitions и строки раньше точного UTC
|
||||
cutoff. Очистка всех выбранных типов выполняется атомарно.
|
||||
|
||||
### 3.4. Runtime и Bootstrap
|
||||
|
||||
Live и Recovery используют один `TradeObservationSinkProtocol` через
|
||||
общий `TradeStreamConsistencyController`. Единственный владелец записи
|
||||
обеспечивает одинаковую последовательность для обоих путей данных.
|
||||
|
||||
Синхронная PostgreSQL-запись выполняется вне asyncio event loop через
|
||||
принадлежащую Runtime задачу. Cancellation ожидает уже начатую запись и
|
||||
не оставляет фонового worker.
|
||||
|
||||
Storage включается только явным `MARKET_DATA_STORAGE_ENABLED=true` и
|
||||
требует включённый Trade Stream. При выключенном флаге зависимости не
|
||||
строятся, pool не открывается, migrations не запускаются.
|
||||
|
||||
---
|
||||
|
||||
## 4. Real PostgreSQL verification
|
||||
|
||||
Безопасный opt-in harness требует одновременно:
|
||||
|
||||
- `DZENTRA_RUN_POSTGRES_TESTS=1`;
|
||||
- отдельный `DZENTRA_TEST_POSTGRES_DSN`;
|
||||
- явный localhost, loopback address или local socket;
|
||||
- имя базы с префиксом `dzentra_test_`.
|
||||
|
||||
PostgreSQL service, неявный endpoint и смешанный local/remote fallback
|
||||
запрещены. Перед destructive cleanup harness повторно проверяет точное
|
||||
имя фактической базы и специальное имя контрольного соединения.
|
||||
|
||||
Настоящий PostgreSQL подтвердил:
|
||||
|
||||
- атомарность migrations и repositories;
|
||||
- участие обеих concurrent migration/partition caller-сессий;
|
||||
- insert, duplicate, provenance и conflict paths;
|
||||
- перенос default rows и точность retention cutoff;
|
||||
- Runtime persistence через Live и reconnect/recovery;
|
||||
- fatal propagation database failure;
|
||||
- правильное закрытие pool и отсутствие оставшихся соединений.
|
||||
|
||||
---
|
||||
|
||||
## 5. Финальные результаты
|
||||
|
||||
Итоговая приёмка выполнена 2026-08-01:
|
||||
|
||||
```text
|
||||
Targeted Storage/Runtime unit suite: 307 passed
|
||||
PostgreSQL control run: 15 passed
|
||||
PostgreSQL repeated ten times: 150 passed
|
||||
Combined integration suite: 26 passed
|
||||
Fixed stress target: 3 passed, 1 deselected
|
||||
PostgreSQL suite without opt-in: 15 skipped
|
||||
Full offline regression: 2119 passed, 31 deselected
|
||||
git diff --check: clean
|
||||
Untracked files diff check: clean
|
||||
```
|
||||
|
||||
В integration и stress наборах `ResourceWarning` считался ошибкой.
|
||||
Одноразовый PostgreSQL 16 работал на `tmpfs` и после приёмки был
|
||||
остановлен и удалён. Итоговый read-only review не обнаружил открытых
|
||||
findings.
|
||||
|
||||
---
|
||||
|
||||
## 6. Границы Build
|
||||
|
||||
Build 060.27 намеренно не реализует:
|
||||
|
||||
- persistent Runtime checkpoint;
|
||||
- Startup Recovery после перезапуска процесса;
|
||||
- Historical Queries и Data Access Layer;
|
||||
- Replay API и Analytics API;
|
||||
- автоматический partition/retention scheduler;
|
||||
- подключение существующих Quote/Candle consumers;
|
||||
- хранение raw exchange documents.
|
||||
|
||||
Persistent Checkpoint и Startup Recovery относятся к Build 060.28.
|
||||
Historical Access и Replay относятся к Build 060.29. Полная итоговая
|
||||
ревизия документации подсистемы остаётся задачей Build 060.30.
|
||||
|
||||
Постороннее пользовательское изменение `.gitignore` не относится к
|
||||
Build 060.27 и не должно включаться в его staging.
|
||||
|
||||
---
|
||||
|
||||
## 7. Итог
|
||||
|
||||
Build 060.27 завершён и принят.
|
||||
|
||||
Dzentra получила транзакционное долговременное хранилище Canonical
|
||||
Market Data и production persistence для Trades. Operational checkpoint
|
||||
теперь продвигается только после подтверждённой durable-записи, а
|
||||
ошибка Storage завершает Runtime без скрытой потери истории.
|
||||
|
||||
Следующий этап — Build 060.28, Persistent Checkpoint and Startup
|
||||
Recovery.
|
||||
Reference in New Issue
Block a user