248 lines
11 KiB
Markdown
248 lines
11 KiB
Markdown
# Build 060.28 — Persistent Checkpoint and Startup Recovery
|
||
|
||
**Engineering Migration Report**
|
||
|
||
---
|
||
|
||
## Контроль документа
|
||
|
||
| Свойство | Значение |
|
||
|---|---|
|
||
| Build | 060.28 |
|
||
| Статус | Completed |
|
||
| Подсистема | Market Data Acquisition / Persistent Checkpoint |
|
||
| Компонент | Trade Stream Startup Recovery |
|
||
| Дата завершения | 2026-08-01 |
|
||
| Версия | 1.0 |
|
||
|
||
---
|
||
|
||
## Связанные документы
|
||
|
||
- [Build 060.28 Architecture](build_060_28_architecture.md) — архитектура,
|
||
решения и подробные результаты подэтапов 060.28.0–060.28.8;
|
||
- [Build 060.27 Engineering Migration Report](build_060_27.md) —
|
||
Persistent Market Data Storage;
|
||
- [Dzentra Target Architecture](../architecture/dzentra_target_architecture.md) —
|
||
место Market Data Acquisition в целевой архитектуре Dzentra;
|
||
- [Master Roadmap](../roadmap/master-roadmap.md) — дальнейшая
|
||
последовательность Build.
|
||
|
||
---
|
||
|
||
## 1. Назначение Build
|
||
|
||
Build 060.28 сделал operational checkpoint Trade Stream постоянным и
|
||
добавил восстановление Runtime после перезапуска процесса.
|
||
|
||
PostgreSQL Canonical Trades остаются источником рыночных фактов.
|
||
Persistent checkpoint хранит только подтверждённую точку в этой истории
|
||
и позволяет восстановить bounded deduplication window до подключения к
|
||
бирже.
|
||
|
||
Итоговая последовательность запуска:
|
||
|
||
```text
|
||
PostgreSQL pool + migrations
|
||
↓
|
||
checkpoint hydration или first-adoption
|
||
↓
|
||
WebSocket connect + subscription ACK
|
||
↓
|
||
REST Recovery при закрытом Live gate
|
||
↓
|
||
buffered Live в исходном порядке
|
||
↓
|
||
обычный Production Runtime
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Завершённые подэтапы
|
||
|
||
| Подэтап | Название | Статус |
|
||
|---|---|---|
|
||
| 060.28.0 | Architecture, Contracts and Failure Policy | Accepted |
|
||
| 060.28.1 | PostgreSQL Checkpoint Schema and Migration | Accepted |
|
||
| 060.28.2 | Checkpoint Repository and Atomic Trade Commit | Accepted |
|
||
| 060.28.3 | Consistency Persistence Integration | Accepted |
|
||
| 060.28.4 | State Hydration and Deduplication Restoration | Accepted |
|
||
| 060.28.5 | Startup Recovery and ACK/Live Boundary | Accepted |
|
||
| 060.28.6 | Bootstrap and Lifecycle Integration | Accepted |
|
||
| 060.28.7 | PostgreSQL Restart and Failure Verification | Accepted |
|
||
| 060.28.8 | Final Regression and Acceptance | Accepted |
|
||
|
||
Каждый подэтап проходил отдельный read-only review. Findings
|
||
исправлялись и закрывались regression-тестами до принятия.
|
||
|
||
---
|
||
|
||
## 3. Реализованная архитектура
|
||
|
||
### 3.1. Schema и единый repository
|
||
|
||
Migration 8 создала непартиционированную таблицу
|
||
`market_data.trade_stream_checkpoints` с ключом `(venue, symbol)`.
|
||
Checkpoint ссылается на точную Canonical Trade identity через deferred
|
||
`ON UPDATE/DELETE NO ACTION` foreign key.
|
||
|
||
`PostgresTradeRepository` остаётся единственным владельцем записи
|
||
Trades и реализует узкий `TradeCheckpointStorageProtocol`. Новая Trade и
|
||
продвижение checkpoint выполняются одним соединением и одной
|
||
транзакцией. Expected identity и revision обеспечивают CAS-защиту от
|
||
stale writer; повтор уже зафиксированной candidate Trade идемпотентен.
|
||
|
||
Duplicate Trade обновляет provenance без продвижения persistent или
|
||
in-memory checkpoint.
|
||
|
||
### 3.2. Consistency и Hydration
|
||
|
||
`TradeStreamConsistencyController` продвигает in-memory state только
|
||
после успешного durable commit. Ошибка PostgreSQL является фатальной и
|
||
не скрывается.
|
||
|
||
`TradeStreamStateHydrator` до запуска сети:
|
||
|
||
1. загружает и проверяет persistent checkpoint;
|
||
2. восстанавливает bounded rollover-aware Trade tail;
|
||
3. создаёт временные `TradeStreamState`;
|
||
4. атомарно публикует весь набор в общий State Store.
|
||
|
||
Если после Build 060.27 история уже существует, а checkpoint ещё нет,
|
||
последняя проверенная Trade один раз принимается как revision `1` без
|
||
повторного наблюдения и изменения provenance.
|
||
|
||
### 3.3. Startup Recovery
|
||
|
||
`RuntimeStartupRecoveryCoordinator` выполняет blocking Hydration и REST
|
||
Recovery через принадлежащие Runtime задачи. Cancellation ожидает уже
|
||
начатый worker, поэтому PostgreSQL pool не закрывается под выполняющейся
|
||
операцией.
|
||
|
||
Production Runtime использует одного WebSocket consumer до завершения
|
||
startup. Он ожидает положительный subscription ACK с конечным timeout,
|
||
складывает ранние market-сообщения в ограниченный FIFO, выполняет REST
|
||
Recovery при закрытом общем gate и затем разбирает FIFO в исходном
|
||
порядке.
|
||
|
||
Negative ACK, timeout, переполнение FIFO, повреждённый checkpoint или
|
||
ошибка Recovery завершают startup без открытия Live processing.
|
||
|
||
### 3.4. Bootstrap и lifecycle
|
||
|
||
Отдельный checkpoint feature flag не добавлен. Persistent checkpoint
|
||
включается только вместе с `MARKET_DATA_STORAGE_ENABLED=true` и
|
||
использует тот же экземпляр `PostgresTradeRepository`, что и запись
|
||
Canonical Trades.
|
||
|
||
Composition не выполняет I/O и не создаёт фоновых задач. Application
|
||
сохраняет порядок:
|
||
|
||
```text
|
||
pool open → migrations → Runtime start
|
||
Runtime stop → ожидание workers → pool close
|
||
```
|
||
|
||
Неполная пара Trade sink/checkpoint storage отклоняется до создания
|
||
сетевых компонентов.
|
||
|
||
### 3.5. Partitions и Retention
|
||
|
||
Foreign key временно снимается только внутри транзакции обслуживания
|
||
Trade partitions и восстанавливается до commit с тем же строгим
|
||
контрактом. Попытка Retention удалить активную checkpoint Trade
|
||
откатывает всю операцию.
|
||
|
||
Partition Manager и atomic writer используют единый порядок блокировок:
|
||
|
||
```text
|
||
market_data.trades parent
|
||
↓
|
||
trades_default
|
||
↓
|
||
trade_stream_checkpoints
|
||
```
|
||
|
||
Это исключает deadlock при одновременной записи в существующую месячную
|
||
partition и создании новой.
|
||
|
||
---
|
||
|
||
## 4. Real PostgreSQL verification
|
||
|
||
Безопасный opt-in harness требует отдельную локальную базу с именем
|
||
`dzentra_test_*` и два явных параметра:
|
||
|
||
- `DZENTRA_RUN_POSTGRES_TESTS=1`;
|
||
- `DZENTRA_TEST_POSTGRES_DSN`.
|
||
|
||
Реальный PostgreSQL 16 подтвердил:
|
||
|
||
- restart с восстановлением checkpoint и deduplication tail;
|
||
- first-adoption существующей истории 060.27;
|
||
- отсутствие network I/O до завершения Hydration/adoption;
|
||
- rollback ошибки до commit и восстановление после ошибки после commit;
|
||
- фатальный orphan checkpoint и REST failure при закрытом Live gate;
|
||
- ожидание blocking Hydration при cancellation;
|
||
- rollover-границы `INT32_MAX → INT32_MIN` и `-1 → 0`;
|
||
- защиту активной checkpoint Trade при Partition/Retention;
|
||
- отсутствие deadlock между Partition Manager и atomic writer;
|
||
- отсутствие оставшихся tasks, threads и PostgreSQL connections.
|
||
|
||
---
|
||
|
||
## 5. Финальные результаты
|
||
|
||
Итоговая приёмка выполнена 2026-08-01:
|
||
|
||
```text
|
||
Partition lock-order unit target: 21 passed
|
||
Restart/failure repeated ten times: 90 passed
|
||
Partition/checkpoint race ten times: 10 passed
|
||
Full PostgreSQL Storage integration: 51 passed
|
||
Full integration with PostgreSQL: 64 passed
|
||
PostgreSQL suite without opt-in: 51 skipped
|
||
Fixed stress target: 3 passed, 1 deselected
|
||
Full offline regression: 2305 passed, 69 deselected
|
||
git diff --check: clean
|
||
Untracked files whitespace check: clean
|
||
```
|
||
|
||
В integration и stress наборах `ResourceWarning` считался ошибкой.
|
||
Одноразовый PostgreSQL-контейнер после приёмки остановлен и удалён.
|
||
Итоговый read-only review не обнаружил открытых findings.
|
||
|
||
---
|
||
|
||
## 6. Границы Build
|
||
|
||
Build 060.28 намеренно не реализует:
|
||
|
||
- общий Data Access Layer и Historical Queries;
|
||
- Replay API и детерминированные replay-часы;
|
||
- подключение persistent Quote/Candle consumers;
|
||
- автоматический Retention Scheduler;
|
||
- distributed lease, leader election и HA failover;
|
||
- бесконечный Recovery retry/backoff.
|
||
|
||
Эти обязанности относятся к следующим Build. Полная итоговая ревизия
|
||
документации Market Data Acquisition остаётся задачей Build 060.30.
|
||
|
||
Постороннее пользовательское изменение `.gitignore` не относится к
|
||
Build 060.28 и не должно включаться в его staging.
|
||
|
||
---
|
||
|
||
## 7. Итог
|
||
|
||
Build 060.28 завершён и принят.
|
||
|
||
Dzentra восстанавливает подтверждённое состояние Trade Stream из
|
||
PostgreSQL после перезапуска, заполняет downtime gap через REST и только
|
||
после этого продолжает Live processing. Durable history, persistent
|
||
checkpoint и in-memory state теперь продвигаются в одном проверяемом
|
||
порядке без скрытой потери последовательности.
|
||
|
||
Следующий этап — [Build 060.29](build_060_29.md), Market Data Access and
|
||
Replay.
|