# 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.