852 lines
42 KiB
Markdown
852 lines
42 KiB
Markdown
# Build 060.28 — Persistent Checkpoint and Startup Recovery Architecture
|
||
|
||
**Статус:** Completed
|
||
|
||
**Build:** 060.28
|
||
|
||
**Подсистема:** Market Data Acquisition / Persistent Checkpoint
|
||
|
||
**Дата начала:** 2026-08-01
|
||
|
||
**Дата завершения:** 2026-08-01
|
||
|
||
**Версия документа:** 1.14
|
||
|
||
---
|
||
|
||
## 1. Назначение
|
||
|
||
Build 060.28 сохраняет operational checkpoint Trade Stream в
|
||
PostgreSQL и восстанавливает Runtime после перезапуска процесса.
|
||
|
||
Build использует Canonical Trade history, созданную в 060.27, как более
|
||
достоверный источник рыночных фактов. Persistent checkpoint является
|
||
операционным указателем на подтверждённую строку истории и не заменяет
|
||
Market Data Storage.
|
||
|
||
После завершения Build новый процесс должен:
|
||
|
||
1. проверить persistent checkpoint по durable Trade history;
|
||
2. восстановить checkpoint и deduplication window в Consistency Layer;
|
||
3. подключить WebSocket и подтвердить подписку;
|
||
4. восстановить Trades за время остановки через REST;
|
||
5. только затем продолжить обработку накопленных Live сообщений.
|
||
|
||
---
|
||
|
||
## 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 |
|
||
|
||
060.28.0 зафиксировал immutable `PersistentTradeCheckpoint`, узкий
|
||
runtime-checkable `TradeCheckpointStorageProtocol` и специализированные
|
||
checkpoint conflict/integrity errors. Контракты не импортируют Runtime,
|
||
Recovery, Bootstrap или PostgreSQL implementation.
|
||
|
||
060.28.1 добавил versioned migration 8 с непартиционированной таблицей
|
||
`market_data.trade_stream_checkpoints`. Реальный PostgreSQL подтвердил
|
||
структуру колонок, idempotent migration history, точный foreign key и
|
||
запрет orphan/delete для активной checkpoint Trade.
|
||
|
||
Оба подэтапа приняты после отдельного read-only review и реальной
|
||
PostgreSQL-проверки. Они реализованы без checkpoint repository,
|
||
Consistency integration, Runtime wiring, Bootstrap I/O и изменения
|
||
feature flags.
|
||
|
||
060.28.2 расширил существующий `PostgresTradeRepository`: он остаётся
|
||
единственным владельцем записи Canonical Trades и одновременно
|
||
реализует `TradeCheckpointStorageProtocol`. Trade write, checkpoint
|
||
lock, expected-identity CAS и revision update выполняются через одно
|
||
соединение и одну transaction. Repository распознаёт уже
|
||
зафиксированную candidate Trade как безопасный повтор, восстанавливает
|
||
bounded rollover-aware tails и явно отклоняет stale/ambiguous writer.
|
||
|
||
Подэтап принят после отдельного read-only review и исправления двух
|
||
rollover-tail findings. Durable acceptance anchor использует
|
||
`first_observed_at`, а повторившийся raw Trade ID выбирается только из
|
||
последнего цикла не позже exact checkpoint Trade. Consistency, Runtime,
|
||
Bootstrap и feature flags не изменялись.
|
||
|
||
060.28.3 разделил persistence-вызовы по уже принятому решению
|
||
Consistency Layer. Новая Trade передаётся в атомарный
|
||
`store_trade_and_advance_checkpoint` вместе с точной предыдущей
|
||
`last_trade`, а полный дубликат вызывает только `store_trade` для
|
||
обновления provenance. Storage не повторяет rollover-классификацию.
|
||
Ошибка любой ветви распространяется вызывающему коду; новая Trade не
|
||
попадает в in-memory state до успешного PostgreSQL commit.
|
||
|
||
Подэтап реализован без hydration, Startup Recovery, новых feature flags,
|
||
изменения Bootstrap/settings или lifecycle. Отдельный приёмочный
|
||
read-only review подтвердил границы ответственности, порядок durable
|
||
commit → in-memory checkpoint и полный rollback при CAS-конфликте;
|
||
findings не выявлено, подэтап принят.
|
||
|
||
060.28.4 добавил строгую фабрику `TradeStreamState.from_history`,
|
||
одноразовую атомарную публикацию через `TradeStreamStateStore.initialize`
|
||
и отдельный `TradeStreamStateHydrator`. Hydrator сначала полностью
|
||
готовит состояния всех символов и только затем публикует их в общий
|
||
Store; при ошибке ни один частично собранный state не становится
|
||
доступен Runtime.
|
||
|
||
Для перехода с Build 060.27 storage-контракт получил отдельную операцию
|
||
`adopt_existing_trade_as_checkpoint`. Она создаёт revision `1` на уже
|
||
существующей durable Trade, не выполняет повторную запись наблюдения и
|
||
не меняет `source`, provenance, `first_observed_at` или
|
||
`last_observed_at`. После adoption Hydrator повторно загружает tail уже
|
||
от окончательной persistent-точки и заново проверяет его. Подэтап
|
||
реализован без подключения к Bootstrap, Runtime lifecycle и Startup
|
||
Recovery. Приёмочный review выявил один случай, когда ложный non-tuple
|
||
ответ Storage мог быть принят за пустую историю. После исправления
|
||
только `tuple()` означает отсутствие истории, а `None` и `[]` приводят
|
||
к фатальной integrity-ошибке без публикации state. Повторный review
|
||
findings не выявил; подэтап принят.
|
||
|
||
060.28.5 добавил отдельный `RuntimeStartupRecoveryCoordinator` и
|
||
встроил persistent startup-путь в `TradeStreamProductionRuntime`.
|
||
Hydration выполняется до сетевого подключения. После подписки Runtime
|
||
одним consumer ожидает положительный ACK, сохраняет ранние Live
|
||
документы в ограниченный FIFO, выполняет REST Recovery при закрытом
|
||
общем gate и только затем обрабатывает накопленный Live-поток.
|
||
|
||
Первый приёмочный review выявил расхождение регистра между Hydrator и
|
||
Startup Recovery, способное бесшумно пропустить восстановление для
|
||
lowercase-конфигурации. Все Runtime-границы и Hydrator переведены на
|
||
единый `normalize_symbol`; варианты одного символа в разном регистре
|
||
схлопываются до запуска blocking worker. Regression-тест проходит
|
||
полную цепочку Hydration → exact-key State Store → REST Recovery.
|
||
Повторный независимый review findings не выявил; подэтап принят без
|
||
изменения Bootstrap и settings.
|
||
|
||
060.28.6 подключил persistent checkpoint к Production Bootstrap без
|
||
нового feature flag. При включённом Market Data Storage один экземпляр
|
||
`PostgresTradeRepository` одновременно используется как владелец
|
||
durable Trades и checkpoint storage, а Runtime получает созданный
|
||
Composition `RuntimeStartupRecoveryCoordinator`. Неполный persistent-
|
||
граф отклоняется до создания WebSocket/REST-компонентов.
|
||
|
||
Настройки ACK timeout и вместимости стартового FIFO получили строгую
|
||
валидацию и явные environment-параметры. Сборка приложения по-прежнему
|
||
не открывает PostgreSQL, не выполняет migrations, Hydration или Recovery
|
||
и не создаёт фоновых задач. Application сохраняет порядок pool open +
|
||
migrations → Runtime и Runtime stop + ожидание workers → pool close.
|
||
После исправления устаревшего PostgreSQL integration harness повторный
|
||
read-only review findings не выявил; подэтап принят.
|
||
|
||
060.28.7 добавил opt-in проверку полного Application lifecycle на
|
||
одноразовом PostgreSQL 16: перезапуск на той же durable history,
|
||
first-adoption истории без checkpoint, откат незавершённой transaction,
|
||
повтор после ошибки persistence и cancellation во время blocking I/O.
|
||
Все сценарии используют только явно заданную локальную тестовую базу и
|
||
проверяют отсутствие оставшихся соединений, задач и обработчиков.
|
||
|
||
Первый приёмочный review выявил недостаточно строгую проверку порядка:
|
||
исходные restart-сценарии наблюдали восстановленное состояние только к
|
||
моменту подписки. Два детерминированных regression-сценария теперь
|
||
останавливают соответственно `load_checkpoint` и
|
||
`adopt_existing_trade_as_checkpoint` внутри storage-вызова и до снятия
|
||
barrier подтверждают отсутствие WebSocket/REST I/O. После исправления
|
||
два независимых read-only review findings не выявили; production-код не
|
||
изменялся, подэтап принят.
|
||
|
||
060.28.8 выполнил verification-only матрицу всего Build: десятикратный
|
||
прогон restart/failure-сценариев, полный PostgreSQL Storage и общий
|
||
integration-наборы, opt-out isolation, fixed stress и полную стандартную
|
||
регрессию. Итоговый review выявил один потенциальный PostgreSQL deadlock
|
||
между Partition Manager и атомарным Trade/checkpoint writer.
|
||
|
||
Partition Manager переведён на единый порядок блокировок parent Trades →
|
||
default partition → checkpoint table. Детерминированный PostgreSQL-тест
|
||
подтвердил реальное участие обоих конкурентных callers и отсутствие
|
||
deadlock; сценарий стабильно прошёл десять повторов. После повторения
|
||
всей приёмочной матрицы итоговый review findings не выявил. Подэтап и
|
||
Build 060.28 приняты.
|
||
|
||
---
|
||
|
||
## 3. Исходные инварианты
|
||
|
||
Build продолжает принятые решения 060.24–060.27:
|
||
|
||
1. Operational checkpoint принадлежит только
|
||
`TradeStreamState.last_trade`.
|
||
2. Live и Recovery используют один `TradeStreamStateStore` и один
|
||
`TradeStreamConsistencyController`.
|
||
3. Canonical Trade сохраняется до продвижения in-memory checkpoint.
|
||
4. Ошибка включённого persistent storage является фатальной.
|
||
5. Идентичность durable Trade равна
|
||
`(venue, symbol, trade_id, executed_at)`.
|
||
6. Порядок Trade ID использует единый signed 32-bit rollover-aware
|
||
контракт.
|
||
7. PostgreSQL pool открывается до Runtime и закрывается только после
|
||
полной остановки Runtime.
|
||
8. Composition не выполняет I/O и не создаёт фоновых задач.
|
||
|
||
---
|
||
|
||
## 4. Иерархия достоверности
|
||
|
||
```text
|
||
PostgreSQL Canonical Trades
|
||
│ подтверждённые рыночные факты
|
||
▼
|
||
Persistent Trade Stream Checkpoint
|
||
│ операционный указатель
|
||
▼
|
||
TradeStreamState
|
||
│ оперативное состояние процесса
|
||
▼
|
||
Live / Recovery processing
|
||
```
|
||
|
||
Persistent checkpoint нельзя использовать, если соответствующая
|
||
Canonical Trade отсутствует или отличается от durable history.
|
||
|
||
Checkpoint не хранит независимую копию цены, количества, стороны и
|
||
source. Он хранит точную identity Trade. Checkpoint Service получает
|
||
полный Canonical `Trade` соединением checkpoint с `market_data.trades`.
|
||
|
||
---
|
||
|
||
## 5. Архитектурная граница
|
||
|
||
```text
|
||
Consistency Layer
|
||
│ acquisition-side Protocol
|
||
▼
|
||
Checkpoint Service
|
||
│ storage contracts
|
||
▼
|
||
PostgreSQL checkpoint repository
|
||
│ общий managed pool
|
||
▼
|
||
market_data.trade_stream_checkpoints
|
||
│ exact durable identity
|
||
▼
|
||
market_data.trades
|
||
```
|
||
|
||
Consistency, Recovery и Runtime не импортируют `psycopg`, PostgreSQL
|
||
repository или pool. PostgreSQL implementation зависит от Canonical
|
||
Models и storage contracts, но не от Runtime lifecycle.
|
||
|
||
Общий `MarketDataStorage` остаётся write-only фасадом Trades, Quotes и
|
||
Candles. Узкие checkpoint reads не превращаются в Historical Query API;
|
||
общий Data Access Layer относится к Build 060.29.
|
||
|
||
---
|
||
|
||
## 6. Storage contracts
|
||
|
||
### 6.1. PersistentTradeCheckpoint
|
||
|
||
Неизменяемый результат чтения checkpoint содержит:
|
||
|
||
```text
|
||
PersistentTradeCheckpoint
|
||
├── venue: str
|
||
├── trade: Trade
|
||
├── revision: int
|
||
├── updated_at: timezone-aware datetime
|
||
└── checkpoint_schema_version: int
|
||
```
|
||
|
||
Полный `Trade` уже проверен по durable history и является checkpoint,
|
||
который можно установить в Consistency state.
|
||
|
||
### 6.2. TradeCheckpointStorageProtocol
|
||
|
||
Узкий storage-контракт предусматривает:
|
||
|
||
```text
|
||
load_checkpoint(venue, symbol)
|
||
load_checkpoint_tail(venue, checkpoint, limit)
|
||
load_latest_trade_tail(venue, symbol, limit)
|
||
adopt_existing_trade_as_checkpoint(venue, trade)
|
||
store_trade_and_advance_checkpoint(
|
||
venue,
|
||
expected_trade,
|
||
trade,
|
||
observed_at,
|
||
)
|
||
```
|
||
|
||
Назначение операций:
|
||
|
||
- `load_checkpoint` возвращает только подтверждённый полный checkpoint;
|
||
- `load_checkpoint_tail` загружает bounded history для восстановления
|
||
deduplication window;
|
||
- `load_latest_trade_tail` используется только при первой инициализации
|
||
после Build 060.27, если история существует, а checkpoint ещё нет;
|
||
- `adopt_existing_trade_as_checkpoint` создаёт первый checkpoint на
|
||
точной уже существующей durable Trade без повторной записи Trade;
|
||
- `store_trade_and_advance_checkpoint` атомарно сохраняет принятую Trade
|
||
и продвигает persistent checkpoint.
|
||
|
||
Основные read/commit-операции реализованы в 060.28.2, а безопасная
|
||
first-adoption — в 060.28.4 существующим `PostgresTradeRepository`.
|
||
Отдельный второй writer не создаётся; общий `MarketDataStorage`
|
||
сохраняет write-only границу, а checkpoint reads доступны только через
|
||
узкий `TradeCheckpointStorageProtocol`.
|
||
|
||
---
|
||
|
||
## 7. PostgreSQL schema
|
||
|
||
Migration 8 создаёт:
|
||
|
||
```text
|
||
market_data.trade_stream_checkpoints
|
||
├── venue TEXT
|
||
├── symbol TEXT
|
||
├── trade_id INTEGER
|
||
├── executed_at TIMESTAMPTZ
|
||
├── revision BIGINT
|
||
├── updated_at TIMESTAMPTZ
|
||
└── checkpoint_schema_version INTEGER
|
||
```
|
||
|
||
Ограничения:
|
||
|
||
```text
|
||
PRIMARY KEY (venue, symbol)
|
||
|
||
FOREIGN KEY (venue, symbol, trade_id, executed_at)
|
||
REFERENCES market_data.trades (
|
||
venue,
|
||
symbol,
|
||
trade_id,
|
||
executed_at
|
||
)
|
||
ON UPDATE NO ACTION
|
||
ON DELETE NO ACTION
|
||
DEFERRABLE INITIALLY DEFERRED
|
||
```
|
||
|
||
Дополнительно проверяются:
|
||
|
||
- непустые `venue` и `symbol`;
|
||
- signed 32-bit диапазон `trade_id`;
|
||
- положительные `revision` и `checkpoint_schema_version`;
|
||
- timezone semantics через PostgreSQL `TIMESTAMPTZ`.
|
||
|
||
Таблица не партиционируется: для `(venue, symbol)` существует ровно одна
|
||
маленькая operational row.
|
||
|
||
---
|
||
|
||
## 8. Атомарное продвижение
|
||
|
||
Для новой принятой Trade `PostgresTradeRepository` выполняет:
|
||
|
||
```text
|
||
BEGIN
|
||
│
|
||
├── store Canonical Trade
|
||
├── lock current checkpoint row
|
||
├── verify expected previous Trade identity
|
||
├── advance checkpoint and revision
|
||
└── COMMIT
|
||
↓
|
||
advance in-memory TradeStreamState
|
||
```
|
||
|
||
Если Trade write или checkpoint update завершается ошибкой, вся
|
||
PostgreSQL transaction откатывается, а in-memory state не изменяется.
|
||
|
||
Точный дубликат продолжает обновлять provenance в `market_data.trades`,
|
||
но не продвигает persistent или in-memory checkpoint.
|
||
|
||
`expected_trade` защищает от stale writer. Если database checkpoint не
|
||
равен ожидаемому предыдущему checkpoint, операция завершается явным
|
||
конфликтом. Это защита от повреждения, а не distributed leader election.
|
||
|
||
---
|
||
|
||
## 9. Восстановление Consistency state
|
||
|
||
Одного `last_trade` недостаточно. После перезапуска REST Recovery может
|
||
вернуть более старые Trades у общей временной границы. Поэтому Startup
|
||
должен восстановить bounded deduplication tail до checkpoint.
|
||
|
||
Правила:
|
||
|
||
1. Загружается не более текущего `deduplication_window_size`.
|
||
2. Порядок нормализуется общим rollover-aware контрактом.
|
||
3. Проверяются symbol, Canonical payload и конечная checkpoint identity.
|
||
4. Сначала собирается новый временный `TradeStreamState`.
|
||
5. Store получает состояние только после полной успешной проверки.
|
||
6. Hydration не вызывает persistence sink и не записывает provenance.
|
||
7. Первый checkpoint на истории 060.27 создаётся только после строгой
|
||
проверки временного state.
|
||
8. После first-adoption tail повторно загружается относительно уже
|
||
созданного checkpoint и проверяется заново.
|
||
9. Все символы публикуются одним `initialize`; ошибка одного символа не
|
||
оставляет частично заполненный State Store.
|
||
|
||
Обязательные границы порядка:
|
||
|
||
```text
|
||
INT32_MAX → INT32_MIN
|
||
-1 → 0
|
||
```
|
||
|
||
---
|
||
|
||
## 10. Первый запуск после Build 060.27
|
||
|
||
### 10.1. Нет checkpoint и нет Trades
|
||
|
||
Состояние остаётся пустым. REST backfill без известной начальной границы
|
||
не выполняется. Первый принятый Live Trade создаёт первый checkpoint.
|
||
|
||
### 10.2. Нет checkpoint, но есть durable Trades
|
||
|
||
Checkpoint Service загружает bounded latest tail, проверяет его порядок
|
||
и один раз создаёт checkpoint на последней подтверждённой Trade. Этот
|
||
путь предназначен для перехода с завершённого Build 060.27.
|
||
|
||
### 10.3. Checkpoint существует
|
||
|
||
Checkpoint обязан ссылаться на существующую точную Canonical Trade.
|
||
Отсутствие строки, несовпадение payload или неоднозначный rollover-order
|
||
являются фатальной ошибкой startup. Тихий переход к пустому состоянию
|
||
запрещён.
|
||
|
||
---
|
||
|
||
## 11. Startup Recovery sequence
|
||
|
||
```text
|
||
open PostgreSQL pool
|
||
↓
|
||
apply migrations
|
||
↓
|
||
load checkpoint and deduplication tail
|
||
↓
|
||
hydrate TradeStreamState
|
||
↓
|
||
connect WebSocket
|
||
↓
|
||
send Trade subscription
|
||
↓
|
||
receive successful ACK
|
||
↓
|
||
capture recovery_end_time
|
||
↓
|
||
REST Recovery under shared live gate
|
||
↓
|
||
process buffered WebSocket market messages
|
||
↓
|
||
start normal receive / Supervisor / Scheduler lifecycle
|
||
```
|
||
|
||
До завершения Recovery существует только один consumer WebSocket.
|
||
Market messages, необходимые для получения последующего ACK, временно
|
||
буферизуются в исходном порядке и не передаются в Consistency Layer.
|
||
|
||
ACK имеет конечный timeout. Negative ACK, timeout, checkpoint error или
|
||
Recovery error являются terminal startup failure.
|
||
|
||
REST Recovery использует существующие `TradeRecoveryWindowPlanner`,
|
||
`TradeRecoveryController` и общий Consistency state. При crash посреди
|
||
Recovery следующий процесс начинает с последней атомарно сохранённой
|
||
checkpoint Trade.
|
||
|
||
---
|
||
|
||
## 12. Retention policy
|
||
|
||
Retention не может удалить Trade, на которую указывает активный
|
||
checkpoint.
|
||
|
||
Защита состоит из трёх уровней:
|
||
|
||
1. `FOREIGN KEY ... ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED`
|
||
сохраняет ссылочную целостность и позволяет фиксировать Trade и
|
||
checkpoint одной transaction;
|
||
2. PostgreSQL создаёт внутренние FK-зависимости от каждой Trade
|
||
partition, поэтому Partition Manager и Retention Service под общим
|
||
advisory lock транзакционно снимают FK только на время переноса или
|
||
удаления секции и восстанавливают его с тем же строгим контрактом до
|
||
commit;
|
||
3. повторное создание FK проверяет всю checkpoint table. Если Retention
|
||
затронул активную checkpoint Trade, проверка завершается ошибкой и
|
||
PostgreSQL откатывает удаление данных, registry и изменение FK.
|
||
|
||
Partition Manager сначала блокирует parent `market_data.trades` в
|
||
`SHARE ROW EXCLUSIVE`, затем default Trade partition и checkpoint table.
|
||
Этот порядок согласован с реализованной атомарной записью Trade →
|
||
checkpoint и исключает взаимную блокировку с writer в существующей
|
||
месячной partition.
|
||
|
||
Попытка удалить активную checkpoint Trade откатывает всю retention-
|
||
операцию. Автоматическое удаление или откат checkpoint запрещены.
|
||
|
||
---
|
||
|
||
## 13. Feature flag и lifecycle
|
||
|
||
Отдельный checkpoint feature flag не вводится.
|
||
|
||
```text
|
||
MARKET_DATA_STORAGE_ENABLED=false
|
||
→ прежний полностью in-memory Runtime
|
||
|
||
MARKET_DATA_STORAGE_ENABLED=true
|
||
→ durable Trades + Persistent Checkpoint + Startup Recovery
|
||
```
|
||
|
||
Таким образом persistent checkpoint нельзя включить без durable Market
|
||
Data. Существующий Storage flag остаётся выключенным по умолчанию.
|
||
|
||
Composition только строит зависимости. Checkpoint I/O начинается после
|
||
открытия pool и migrations. Shutdown сохраняет существующий порядок:
|
||
|
||
```text
|
||
Trade Runtime stop
|
||
↓
|
||
await all persistence/recovery workers
|
||
↓
|
||
PostgreSQL pool close
|
||
```
|
||
|
||
Checkpoint не создаёт Scheduler, periodic flush или fire-and-forget
|
||
задачи. Каждая принятая Trade фиксируется inline до in-memory checkpoint.
|
||
|
||
---
|
||
|
||
## 14. Ошибки и конкурентность
|
||
|
||
Фатальными являются:
|
||
|
||
- checkpoint с отсутствующей durable Trade;
|
||
- несовпадающая checkpoint identity или Canonical payload;
|
||
- stale expected checkpoint;
|
||
- ошибка атомарной Trade/checkpoint transaction;
|
||
- неоднозначный signed rollover-order;
|
||
- неуспешная hydration;
|
||
- ошибка Startup Recovery;
|
||
- ACK timeout или negative ACK.
|
||
|
||
Повторная загрузка checkpoint является read-only и идемпотентна.
|
||
Повтор атомарного commit после неоднозначного сетевого результата должен
|
||
распознавать уже зафиксированную ту же candidate identity.
|
||
|
||
Build предполагает одного активного Trade Runtime для `(venue, symbol)`.
|
||
Optimistic checkpoint conflict не даёт второму экземпляру незаметно
|
||
повредить состояние. Distributed lease и автоматический failover не
|
||
входят в scope.
|
||
|
||
---
|
||
|
||
## 15. Критерии приёмки
|
||
|
||
1. Storage contracts не импортируют Runtime, Recovery или Bootstrap.
|
||
2. Checkpoint table имеет точный ключ `(venue, symbol)`.
|
||
3. Checkpoint ссылается на durable Trade identity.
|
||
4. Schema запрещает пустые identity fields и неверные версии.
|
||
5. Migration остаётся versioned, атомарной и идемпотентной.
|
||
6. Accepted Trade и checkpoint фиксируются одной transaction.
|
||
7. Ошибка checkpoint откатывает новую Trade.
|
||
8. Duplicate provenance не продвигает checkpoint.
|
||
9. Stale concurrent writer получает явный conflict.
|
||
10. Hydration восстанавливает checkpoint и deduplication tail.
|
||
11. Rollover boundaries восстанавливаются в правильном порядке.
|
||
12. Повреждённый persistent checkpoint не скрывается.
|
||
13. Startup Recovery заканчивается до buffered Live processing.
|
||
14. Cancellation ожидает уже начатые blocking workers.
|
||
15. Retention не удаляет активную checkpoint Trade.
|
||
16. Composition не выполняет PostgreSQL или network I/O.
|
||
17. Runtime полностью останавливается до pool close.
|
||
18. После тестов не остаются asyncio tasks, threads или connections.
|
||
19. Целевые, integration и полная regression проходят успешно.
|
||
|
||
---
|
||
|
||
## 16. Test strategy
|
||
|
||
### Unit
|
||
|
||
- value object и runtime-checkable Protocol;
|
||
- schema SQL и migration order;
|
||
- repository load/commit/CAS и полный rollback;
|
||
- hydration и bounded deduplication tail;
|
||
- no-checkpoint и first-adoption policy;
|
||
- ACK timeout, buffering, ordering и cancellation.
|
||
|
||
### PostgreSQL integration
|
||
|
||
- реальный foreign key и transactional commit;
|
||
- concurrent first insert и stale update;
|
||
- crash boundaries до и после commit;
|
||
- restart с той же disposable database;
|
||
- retention active-checkpoint protection;
|
||
- отсутствие оставшихся pool connections.
|
||
|
||
### Runtime integration
|
||
|
||
- первый процесс сохраняет Live Trades и завершается;
|
||
- второй процесс восстанавливает checkpoint и deduplication tail;
|
||
- REST заполняет downtime gap;
|
||
- buffered Live Trades обрабатываются после Recovery;
|
||
- duplicate boundary обновляет provenance один раз;
|
||
- failure не открывает live gate и не оставляет owned tasks.
|
||
|
||
---
|
||
|
||
## 17. Вне scope
|
||
|
||
- общие Historical Queries и Data Access Layer;
|
||
- Replay API и replay clocks;
|
||
- persistent Quotes/Candles consumers;
|
||
- автоматический Retention Scheduler;
|
||
- distributed runtime lease, leader election и HA failover;
|
||
- бесконечный REST retry/backoff;
|
||
- восстановление истории без известной durable начальной границы.
|
||
|
||
Эти обязанности относятся к следующим Build либо отдельным production
|
||
инициативам.
|
||
|
||
---
|
||
|
||
## 18. Архитектурные решения
|
||
|
||
### ADR-060.28-001 — Durable Trades являются источником рыночных фактов
|
||
|
||
**Статус:** Accepted
|
||
|
||
Persistent checkpoint хранит identity и проверяется по Canonical Trade
|
||
history. Он не заменяет Market Data Storage.
|
||
|
||
### ADR-060.28-002 — Trade и persistent checkpoint фиксируются атомарно
|
||
|
||
**Статус:** Accepted
|
||
|
||
Accepted Trade и её checkpoint принадлежат одной PostgreSQL transaction.
|
||
In-memory checkpoint продвигается только после commit.
|
||
|
||
### ADR-060.28-003 — Consistency остаётся владельцем checkpoint
|
||
|
||
**Статус:** Accepted
|
||
|
||
Storage хранит operational pointer, но решение о принятии Trade и
|
||
продвижении состояния остаётся в Consistency Layer.
|
||
|
||
### ADR-060.28-004 — Startup восстанавливает deduplication tail
|
||
|
||
**Статус:** Accepted
|
||
|
||
Восстановление только `last_trade` недостаточно для безопасной общей
|
||
границы REST и Live.
|
||
|
||
### ADR-060.28-005 — Startup Recovery завершается до buffered Live
|
||
|
||
**Статус:** Accepted
|
||
|
||
Подписка подтверждается ACK, затем REST Recovery обрабатывается при
|
||
закрытом общем gate и только после этого разрешаются Live Trades.
|
||
|
||
### ADR-060.28-006 — Retention не удаляет активную точку восстановления
|
||
|
||
**Статус:** Accepted
|
||
|
||
Удаление checkpoint Trade запрещено ссылочной целостностью. Во время
|
||
partition maintenance Retention Service восстанавливает и повторно
|
||
проверяет тот же FK до commit; ошибка откатывает всю transaction.
|
||
|
||
### ADR-060.28-007 — Отдельный checkpoint feature flag не вводится
|
||
|
||
**Статус:** Accepted
|
||
|
||
Persistent Checkpoint является частью явно включённого Market Data
|
||
Storage и не может работать без durable Trades.
|
||
|
||
### ADR-060.28-008 — Optimistic conflict защищает от второго writer
|
||
|
||
**Статус:** Accepted
|
||
|
||
Checkpoint update проверяет ожидаемую previous identity. Полноценный
|
||
distributed lease не входит в Build.
|
||
|
||
---
|
||
|
||
## 19. Verification evidence
|
||
|
||
Результаты реализации 060.28.0–060.28.1 на 2026-08-01:
|
||
|
||
```text
|
||
Checkpoint contracts and migration unit target: 51 passed
|
||
Expanded Storage unit target: 255 passed
|
||
Targeted PostgreSQL schema/partition/retention: 12 passed
|
||
Full PostgreSQL Storage integration: 20 passed
|
||
git diff --check: clean
|
||
```
|
||
|
||
PostgreSQL 16 запускался в одноразовом локальном контейнере. После
|
||
проверки foreign key, idempotent migrations, переноса checkpoint Trade
|
||
между default/monthly partitions и Retention rollback контейнер
|
||
остановлен и удалён. Повторный read-only review findings не выявил.
|
||
Checkpoint repository, Runtime и Bootstrap в этих подэтапах не
|
||
подключались.
|
||
|
||
Дополнительные результаты реализации 060.28.2:
|
||
|
||
```text
|
||
Checkpoint repository unit target: 69 passed
|
||
Expanded Storage unit target: 275 passed
|
||
Real PostgreSQL checkpoint repository target: 10 passed
|
||
Full PostgreSQL Storage integration: 30 passed
|
||
git diff --check: clean
|
||
```
|
||
|
||
Реальный PostgreSQL подтвердил атомарный rollback Trade при checkpoint
|
||
failure/conflict, идемпотентный повтор после commit, два конкурирующих
|
||
первых writer, два конкурирующих CAS update и границы
|
||
`INT32_MAX → INT32_MIN` / `-1 → 0`. PostgreSQL-контейнер после проверки
|
||
остановлен и удалён. Consistency, Runtime и Bootstrap не изменялись.
|
||
|
||
Финальный review дополнительно подтвердил порядок при убывающем
|
||
`executed_at` и растущем Trade ID, а также исключение старого полного
|
||
32-битного цикла при повторе того же raw ID. Повторный review findings
|
||
не выявил; 060.28.2 принят.
|
||
|
||
Результаты реализации 060.28.3:
|
||
|
||
```text
|
||
Targeted Consistency/Storage unit: 104 passed
|
||
Full unit regression: 2169 passed
|
||
Targeted PostgreSQL Consistency integration: 3 passed
|
||
Existing PostgreSQL Runtime persistence: 4 passed
|
||
Full PostgreSQL Storage integration: 33 passed
|
||
git diff --check: clean
|
||
```
|
||
|
||
Реальный PostgreSQL подтвердил синхронное продвижение persistent и
|
||
in-memory checkpoint, сохранение provenance дубликата без изменения
|
||
revision и полный rollback candidate Trade при CAS-конфликте. Отдельный
|
||
приёмочный read-only review findings не выявил; 060.28.3 принят.
|
||
|
||
Результаты реализации 060.28.4:
|
||
|
||
```text
|
||
Targeted hydration/repository unit: 140 passed
|
||
Full unit regression: 2209 passed
|
||
Targeted PostgreSQL adoption/hydration: 18 passed
|
||
Full PostgreSQL Storage integration: 41 passed
|
||
git diff --check: clean
|
||
```
|
||
|
||
Unit-тесты подтвердили strict hydration, восстановление bounded dedup
|
||
window, атомарную публикацию нескольких символов, ошибки каждого шага и
|
||
границы `INT32_MAX → INT32_MIN` / `-1 → 0`. Реальный PostgreSQL
|
||
подтвердил неизменность provenance и observation timestamps при
|
||
first-adoption, идемпотентный restart без роста revision, восстановление
|
||
дубликатов после restart и последующее атомарное продвижение revision
|
||
`1 → 2`. Дополнительные regression-тесты подтвердили запрет falsey
|
||
non-tuple ответа Storage без fallback к пустому state. Повторный
|
||
read-only review findings не выявил; 060.28.4 принят. Runtime,
|
||
Bootstrap, settings и Startup Recovery не изменялись.
|
||
|
||
Результаты реализации 060.28.5:
|
||
|
||
```text
|
||
Targeted Runtime/Checkpoint unit: 257 passed
|
||
Loopback Runtime integration: 13 passed
|
||
Critical startup ordering repeated: 10/10
|
||
Full standard regression: 2263 passed
|
||
Deselected integration/stress/live tests: 59
|
||
git diff --check: clean
|
||
```
|
||
|
||
Проверены Hydration до первого network I/O, положительный и ошибочный
|
||
ACK, ограниченный FIFO ранних Live-сообщений, строгий порядок
|
||
Recovery → buffered Live, частичный startup, cancellation blocking
|
||
workers, cleanup owned tasks и legacy-путь без persistent checkpoint.
|
||
Отдельные regression-сценарии подтвердили единый uppercase-ключ для
|
||
lowercase symbols и безопасное схлопывание case-variant duplicates до
|
||
Hydration. Два независимых повторных read-only review findings не
|
||
выявили; 060.28.5 принят.
|
||
|
||
Результаты реализации 060.28.6:
|
||
|
||
```text
|
||
Targeted PostgreSQL Application lifecycle: 1 passed
|
||
Full PostgreSQL Storage integration: 41 passed
|
||
Full integration with PostgreSQL: 54 passed
|
||
Full standard regression: 2276 passed
|
||
Deselected integration/stress/live tests: 59
|
||
git diff --check: clean
|
||
```
|
||
|
||
Подтверждены единый persistent dependency graph, отсутствие I/O и
|
||
фоновых задач при Bootstrap-сборке, строгий fail-fast для неполной
|
||
пары sink/checkpoint storage, Hydration и Startup Recovery до Live
|
||
processing, а также закрытие PostgreSQL pool только после полной
|
||
остановки Runtime. Повторная cancellation не прерывает cleanup.
|
||
Исправленный реальный Application-сценарий дополнительно подтвердил
|
||
отсутствие оставшихся owned tasks и соединений PostgreSQL. Два
|
||
повторных read-only review findings не выявили; 060.28.6 принят.
|
||
|
||
Результаты реализации 060.28.7:
|
||
|
||
```text
|
||
PostgreSQL test-support unit: 52 passed
|
||
Persistent restart/failure scenarios: 9 passed
|
||
Network-order barriers repeated: 10 x 2 passed
|
||
Full PostgreSQL Storage integration: 50 passed
|
||
Full integration with PostgreSQL: 63 passed
|
||
PostgreSQL opt-out isolation: 50 skipped
|
||
Full standard regression: 2305 passed
|
||
Deselected integration/stress/live tests: 68
|
||
git diff --check: clean
|
||
```
|
||
|
||
Реальный PostgreSQL подтвердил восстановление checkpoint и bounded
|
||
deduplication tail после перезапуска, безопасную first-adoption,
|
||
атомарный rollback, повтор после persistence failure и завершение уже
|
||
начатого blocking worker при cancellation. Два новых barrier-сценария
|
||
отдельно доказали, что Hydration и first-adoption завершаются до первого
|
||
WebSocket/REST I/O; каждый сценарий стабильно прошёл десять повторов.
|
||
Opt-out запуск пропустил все 50 PostgreSQL-тестов без подключения к базе.
|
||
Одноразовый контейнер после проверки остановлен и удалён. Финальный
|
||
приёмочный review findings не выявил; 060.28.7 принят.
|
||
|
||
Финальные результаты 060.28.8 после исправления lock order:
|
||
|
||
```text
|
||
Partition lock-order unit target: 21 passed
|
||
Restart/failure scenarios repeated: 10 x 9 passed
|
||
Partition/checkpoint race repeated: 10 x 1 passed
|
||
Full PostgreSQL Storage integration: 51 passed
|
||
Full integration with PostgreSQL: 64 passed
|
||
PostgreSQL opt-out isolation: 51 skipped
|
||
Fixed stress target: 3 passed, 1 deselected
|
||
Full standard regression: 2305 passed
|
||
Deselected integration/stress/live tests: 69
|
||
git diff --check: clean
|
||
Untracked files whitespace check: clean
|
||
```
|
||
|
||
В integration и stress наборах `ResourceWarning` считался ошибкой.
|
||
Одноразовый PostgreSQL 16 после проверки остановлен и удалён. Итоговый
|
||
review полного Build подтвердил архитектурные границы, атомарность,
|
||
CAS/rollover, Startup Recovery, lifecycle/cancellation, Retention и
|
||
единый порядок блокировок; открытых findings не осталось.
|
||
|
||
---
|
||
|
||
## 20. Следующий Build
|
||
|
||
Build 060.28 завершён и принят. Следующий этап — Build 060.29, Market
|
||
Data Access and Replay.
|