511 lines
29 KiB
Markdown
511 lines
29 KiB
Markdown
# Build 060.27 — Persistent Market Data Storage Architecture
|
||
|
||
**Статус:** Accepted
|
||
|
||
**Build:** 060.27
|
||
|
||
**Подсистема:** Market Data Acquisition / Persistent Storage
|
||
|
||
**Дата начала:** 2026-07-31
|
||
|
||
**Дата завершения:** 2026-08-01
|
||
|
||
**Версия документа:** 1.5
|
||
|
||
---
|
||
|
||
## 1. Назначение
|
||
|
||
Build 060.27 создаёт долговременное PostgreSQL-хранилище Canonical
|
||
Market Data. Данные должны сохраняться независимо от времени жизни
|
||
процесса и стать основой для Persistent Checkpoint, исторических
|
||
запросов и Replay в следующих Build.
|
||
|
||
В Build реализуется production-интеграция только для Trades. Для Quotes
|
||
и Candles создаются схема, repository-контракты и Storage API, но
|
||
существующие legacy/runtime consumers пока к ним не подключаются.
|
||
|
||
---
|
||
|
||
## 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 |
|
||
|
||
060.27.0–060.27.1 прошли отдельный read-only review. После исправления
|
||
cleanup при системном прерывании и удаления двух дублирующих индексов
|
||
повторный review завершён без findings.
|
||
|
||
060.27.2 прошёл отдельный read-only review конкурентных вставок,
|
||
Canonical conflict, provenance, batch atomicity и error propagation.
|
||
Review завершён без findings.
|
||
|
||
060.27.3 прошёл отдельный read-only review Quote timestamp collision,
|
||
Candle revision identity, interval case, concurrent provenance и
|
||
transaction rollback. Review завершён без findings.
|
||
|
||
060.27.4 принят после исправления проверки фактического PostgreSQL
|
||
partition bound и изоляции Retention от concurrent writers. Повторный
|
||
read-only review concurrent creation, default-row move, DDL safety,
|
||
точности cutoff и полного rollback завершён без findings.
|
||
|
||
060.27.5 принят после отдельного read-only review durable-before-
|
||
checkpoint ordering, duplicate provenance, fatal error propagation,
|
||
cancellation-safe live offload и единственного владельца записи.
|
||
Review завершён без findings; целевые тесты и полная регрессия прошли.
|
||
|
||
060.27.6 реализует disabled-by-default Storage feature flag, общий
|
||
bootstrap graph и управляемый Application lifecycle. Целевые тесты и
|
||
полная регрессия прошли. Отдельный read-only review feature flag,
|
||
отсутствия Storage I/O при composition, partial startup, shutdown order,
|
||
repeated cancellation и task ownership завершён без findings.
|
||
|
||
060.27.7 добавляет безопасный opt-in PostgreSQL harness и выполняет
|
||
реальные schema, repository, partition, retention, Runtime persistence и
|
||
failure-сценарии на одноразовой локальной базе. Настоящий PostgreSQL
|
||
выявил ошибку параметризации partition DDL; границы CHECK и ATTACH теперь
|
||
встраиваются как безопасные `psycopg.sql.Literal`.
|
||
|
||
Первичный review выявил обход local-only проверки через PostgreSQL
|
||
service/неявный endpoint и недостаточное доказательство одновременного
|
||
участия concurrent callers. Harness теперь запрещает service, неявные и
|
||
смешанные local/remote endpoints, а непосредственно перед destructive
|
||
cleanup повторно сверяет точное имя базы и identity контрольного
|
||
соединения. Migration и partition tests удерживают advisory lock, пока
|
||
PostgreSQL не подтвердит ожидание обеих caller-сессий; Trade writers
|
||
синхронизируются общим барьером. Повторный review завершён без findings,
|
||
подэтап принят.
|
||
|
||
060.27.8 выполнил финальную verification-only приёмку без изменения
|
||
production-кода. Целевой unit-набор, новый одноразовый PostgreSQL,
|
||
десять повторов PostgreSQL integration, общий integration, fixed stress
|
||
и полная offline-регрессия прошли. Итоговый read-only review не выявил
|
||
новых findings; подэтап и Build 060.27 приняты.
|
||
|
||
---
|
||
|
||
## 3. Принятые архитектурные решения
|
||
|
||
1. В production runtime этого Build подключается только хранение Trades.
|
||
2. Для Quotes и Candles создаётся storage foundation без изменения
|
||
существующего legacy/runtime wiring.
|
||
3. Operational checkpoint можно продвигать только после успешного
|
||
сохранения соответствующего Trade.
|
||
4. Ошибка включённого persistent storage является фатальной: Runtime не
|
||
должен скрывать потерю истории и продолжать работу как будто всё
|
||
успешно.
|
||
5. Raw exchange documents в Build 060.27 не сохраняются. Хранилище
|
||
принимает только прошедшие validation и mapping Canonical Models.
|
||
6. Canonical history по умолчанию не удаляется. Retention включается
|
||
только явной настройкой и отдельной операцией.
|
||
7. Идентичность Trade задаётся кортежем `venue`, `symbol`, `trade_id`,
|
||
`executed_at`. Включение времени защищает от коллизии после полного
|
||
цикла signed 32-bit Trade ID.
|
||
8. Market Data размещаются в PostgreSQL schema `market_data`, изменения
|
||
схемы выполняются упорядоченными SQL-миграциями, соединения выдаёт
|
||
один явно управляемый pool.
|
||
|
||
---
|
||
|
||
## 4. Архитектурная граница
|
||
|
||
```text
|
||
Canonical Trade / Quote / Candle
|
||
│
|
||
▼
|
||
market_data.storage contracts
|
||
│
|
||
▼
|
||
PostgreSQL repositories (060.27.2–060.27.3)
|
||
│
|
||
▼
|
||
storage.PostgresConnectionPool
|
||
│
|
||
▼
|
||
PostgreSQL schema market_data
|
||
```
|
||
|
||
Storage зависит от Canonical Models. Canonical Models, Consistency,
|
||
Recovery и Transport не зависят от PostgreSQL или `psycopg`.
|
||
|
||
Trade Runtime использует общий acquisition-side контракт
|
||
`TradeObservationSinkProtocol`. Его Storage adapter знает о
|
||
`TradeStorageProtocol`, venue и UTC clock, но Consistency и Runtime не
|
||
знают о PostgreSQL repository или pool:
|
||
|
||
```text
|
||
Live / Recovery
|
||
│
|
||
▼
|
||
shared TradeStreamConsistencyController
|
||
│ validated observation
|
||
▼
|
||
TradeObservationSinkProtocol
|
||
│
|
||
▼
|
||
TradeStorageObservationSink → TradeStorageProtocol
|
||
```
|
||
|
||
Контракты возвращают явный результат записи:
|
||
|
||
- `INSERTED` — новое событие сохранено;
|
||
- `DUPLICATE` — полностью совпадающее событие уже существовало;
|
||
- `PROVENANCE_UPDATED` — Canonical payload совпал, но обновлена
|
||
информация о наблюдении события.
|
||
|
||
Несовпадающий payload под тем же ключом не является дубликатом и должен
|
||
приводить к `MarketDataStorageConflictError`.
|
||
|
||
---
|
||
|
||
## 5. Модель хранения
|
||
|
||
### 5.1. Trades
|
||
|
||
Таблица `market_data.trades` хранит Canonical Trade и технические поля
|
||
provenance:
|
||
|
||
```text
|
||
identity = venue + symbol + trade_id + executed_at
|
||
provenance = source + observation_sources
|
||
+ first_observed_at + last_observed_at
|
||
```
|
||
|
||
`source` сохраняет первый зафиксированный путь получения Trade.
|
||
`observation_sources` накапливает уникальные Canonical source names,
|
||
например WebSocket и REST Recovery, не превращая различие transport
|
||
provenance в конфликт биржевого факта.
|
||
|
||
`trade_id` ограничен signed 32-bit диапазоном. Это соответствует
|
||
единому rollover-aware контракту Build 060.26, но база не пытается
|
||
сортировать события только по числовому ID. Исторический порядок
|
||
определяется прежде всего `executed_at`.
|
||
|
||
### 5.2. Quotes
|
||
|
||
Таблица `market_data.quotes` хранит неизменяемые Canonical snapshots.
|
||
Ключ снимка: `venue`, `symbol`, `received_at`. Nullable
|
||
`exchange_timestamp` сохраняется как часть payload, но не используется
|
||
как обязательный ключ.
|
||
|
||
`source` сохраняет первый зафиксированный путь получения, а
|
||
`observation_sources` — уникальные источники того же snapshot. Два
|
||
разных payload с одним `received_at` являются явным timestamp conflict;
|
||
repository не изменяет полученное время и не создаёт скрытый surrogate.
|
||
|
||
### 5.3. Candles
|
||
|
||
Свеча может меняться до закрытия интервала. Поэтому
|
||
`market_data.candle_revisions` не перезаписывает строку на месте, а
|
||
хранит последовательность наблюдавшихся ревизий. Ключ:
|
||
|
||
```text
|
||
venue + symbol + interval + open_time + observed_at
|
||
```
|
||
|
||
Флаг `is_final` отделяет промежуточную ревизию от финальной.
|
||
Изменение содержимого или `is_final` сохраняется как новая ревизия с
|
||
новым `observed_at`. Разный source той же ревизии обновляет только
|
||
`observation_sources`. Регистр `interval` сохраняется, потому что имена
|
||
рыночных интервалов могут быть чувствительны к регистру.
|
||
|
||
### 5.4. Партиционирование и retention
|
||
|
||
Все три таблицы объявлены как range-partitioned по времени события и
|
||
имеют default partition. Это даёт безопасную запись до появления
|
||
автоматического partition manager.
|
||
|
||
Default partition остаётся страховкой для записи до создания нужного
|
||
месяца. `PostgresMarketDataPartitionManager` под transaction-level
|
||
advisory lock создаёт отдельную месячную таблицу, блокирует default
|
||
partition, переносит подходящие строки и подключает таблицу как
|
||
partition в одной транзакции.
|
||
|
||
Созданные Dzentra partitions регистрируются в
|
||
`market_data.partition_registry`. Существующая незарегистрированная
|
||
таблица, отсутствующая зарегистрированная таблица или detached
|
||
partition считаются ошибкой конфигурации и не принимаются молча.
|
||
|
||
Canonical retention по умолчанию не ограничен.
|
||
`PostgresMarketDataRetentionService` вообще не обращается к базе при
|
||
выключенной policy. Включённый явный запуск удаляет целиком только
|
||
зарегистрированные истёкшие partitions, затем удаляет строки до точного
|
||
UTC cutoff из оставшихся/default partitions. Все настроенные типы
|
||
очищаются в одной транзакции.
|
||
|
||
---
|
||
|
||
## 6. Версионированные миграции
|
||
|
||
`StorageMigrationRunner`:
|
||
|
||
1. получает соединение через переданный provider;
|
||
2. берёт PostgreSQL transaction-level advisory lock;
|
||
3. создаёт таблицу истории `public.storage_schema_migrations`;
|
||
4. сверяет уже применённые версии и их неизменяемые имена;
|
||
5. применяет отсутствующие версии строго по порядку;
|
||
6. записывает версию в историю в той же транзакции.
|
||
|
||
Неизвестная версия в базе или другое имя уже применённой версии
|
||
считается ошибкой. Миграции не должны молча принимать расхождение
|
||
между кодом и фактической схемой.
|
||
|
||
Начальный набор:
|
||
|
||
| Версия | Назначение |
|
||
|---|---|
|
||
| 1 | schema `market_data` |
|
||
| 2 | partitioned Canonical Trades |
|
||
| 3 | partitioned Canonical Quotes |
|
||
| 4 | partitioned immutable Candle revisions |
|
||
| 5 | multiple Trade observation sources |
|
||
| 6 | Quote and Candle observation sources |
|
||
| 7 | managed monthly partition registry |
|
||
|
||
---
|
||
|
||
## 7. Connection Pool lifecycle
|
||
|
||
`PostgresConnectionPool` имеет явный lifecycle:
|
||
|
||
```text
|
||
construct → open → borrow/return connections → close
|
||
```
|
||
|
||
Конструктор не открывает сеть. `open()` ждёт готовности минимального
|
||
числа соединений и при ошибке освобождает частично созданный pool.
|
||
`connection()` до успешного `open()` запрещён. `close()` идемпотентен.
|
||
|
||
В 060.27.6 pool, migration runner, Trade repository и observation sink
|
||
собираются без сетевых операций. Application запускает blocking startup
|
||
через owned `asyncio.to_thread` task до Telegram и Trade Runtime:
|
||
|
||
```text
|
||
pool.open → migrations.run → Telegram + Trade Runtime
|
||
```
|
||
|
||
Shutdown выполняется в обратном порядке зависимостей. Сначала полностью
|
||
останавливается Trade Runtime и завершаются его Storage-вызовы, затем
|
||
закрывается pool и после этого bot session.
|
||
|
||
---
|
||
|
||
## 8. Обработка ошибок
|
||
|
||
- ошибки lifecycle pool оборачиваются в
|
||
`PostgresConnectionPoolError`;
|
||
- ошибки запуска или расхождения миграций оборачиваются в
|
||
`StorageMigrationError`;
|
||
- repository validation, conflict и database operations используют
|
||
иерархию `MarketDataStorageError`;
|
||
- `KeyboardInterrupt` и `SystemExit` не превращаются в storage errors;
|
||
- ошибка включённого persistent storage фатальна и не скрывается;
|
||
- ошибка cleanup не заменяет исходную startup/runtime ошибку;
|
||
- cancellation ждёт уже начатый blocking lifecycle operation.
|
||
|
||
---
|
||
|
||
## 9. Границы реализации 060.27.0–060.27.8
|
||
|
||
Реализовано:
|
||
|
||
- отдельный пакет `market_data.storage`;
|
||
- write-only Protocol contracts для Trades, Quotes и Candles;
|
||
- результаты одиночной и пакетной идемпотентной записи;
|
||
- отдельная иерархия ошибок Market Data Storage;
|
||
- versioned migration runner с advisory lock;
|
||
- partitioned PostgreSQL schema для трёх Canonical типов;
|
||
- явно открываемый и закрываемый PostgreSQL connection pool;
|
||
- синхронный `PostgresTradeRepository` без ownership pool lifecycle;
|
||
- одиночная и атомарная пакетная запись Canonical Trades;
|
||
- exact duplicate, provenance update и canonical conflict semantics;
|
||
- стабильный порядок batch-записи и rollback всей пачки при ошибке;
|
||
- синхронные `PostgresQuoteRepository` и `PostgresCandleRepository`;
|
||
- immutable Quote snapshot и Candle revision conflict semantics;
|
||
- multiple-source provenance для Trades, Quotes и Candle revisions;
|
||
- общие validation helpers трёх PostgreSQL repositories;
|
||
- единый write-only `MarketDataStorage` facade без lifecycle ownership;
|
||
- UTC monthly partition descriptors для трёх Canonical типов;
|
||
- транзакционное создание, перенос и attach managed partitions;
|
||
- реестр Dzentra-managed partitions в migration 7;
|
||
- disabled-by-default retention с отдельным явным запуском;
|
||
- удаление полных partitions и точная очистка partial/default данных;
|
||
- общий advisory lock для partition lifecycle и retention;
|
||
- закреплённые версии `psycopg` и `psycopg-pool`;
|
||
- общий optional `TradeObservationSinkProtocol` для Live и Recovery;
|
||
- единственный persistence owner в `TradeStreamConsistencyController`;
|
||
- durable write до продвижения operational checkpoint;
|
||
- запись корректных дубликатов для обновления provenance;
|
||
- выполнение синхронной live-записи вне asyncio event loop;
|
||
- cancellation-safe ожидание уже начатой live-записи при shutdown;
|
||
- отдельный `MarketDataStorageSettings` и безопасный feature flag;
|
||
- единый bootstrap graph pool → migrations → Trade repository → sink;
|
||
- использование одного Settings snapshot и существующих `DB_*`;
|
||
- Storage startup до Telegram и Trade Runtime;
|
||
- partial startup cleanup при migration failure;
|
||
- закрытие pool только после полной остановки Trade Runtime;
|
||
- cancellation-safe Application startup и shutdown tasks.
|
||
- opt-in harness только для явно указанной локальной disposable базы;
|
||
- отсутствие fallback PostgreSQL integration-тестов на application
|
||
`DB_*`;
|
||
- real PostgreSQL verification миграций, repositories и транзакций;
|
||
- real concurrent migration, Trade write и partition callers;
|
||
- real partition move, retention cutoff и multi-type rollback;
|
||
- local WebSocket/REST Runtime persistence в настоящий PostgreSQL;
|
||
- real reconnect/recovery provenance через общий Trade repository;
|
||
- fatal persistence failure без ложного продвижения checkpoint;
|
||
- проверка Application ownership pool и отсутствия оставшихся соединений.
|
||
|
||
Не реализовано на этом шаге:
|
||
|
||
- чтение истории;
|
||
- автоматический partition/retention scheduling;
|
||
- подключение legacy Quote/Candle consumers.
|
||
|
||
---
|
||
|
||
## 10. Критерии приёмки 060.27.0–060.27.8
|
||
|
||
1. Storage contracts не импортируют Runtime, Consistency или Recovery.
|
||
2. Конструктор pool не выполняет сетевых операций.
|
||
3. Частично открытый pool очищается при ошибке startup.
|
||
4. Повторный `close()` безопасен.
|
||
5. Миграции выполняются один раз, последовательно и атомарно.
|
||
6. Concurrent migration runners сериализуются advisory lock.
|
||
7. Schema содержит явные idempotency keys и Canonical constraints.
|
||
8. Trade Runtime не импортирует PostgreSQL repository или pool.
|
||
9. Целевые тесты и обычная регрессия проходят успешно.
|
||
10. Источник доставки не является частью Canonical Trade conflict.
|
||
11. Один конфликт или database error откатывает весь Trade batch.
|
||
12. Repository не владеет pool lifecycle и не импортирует Runtime.
|
||
13. Quote timestamp collision и Candle revision conflict не скрываются.
|
||
14. Quote/Candle source обновляет provenance, а не Canonical payload.
|
||
15. Существующие Quote/Candle consumers не подключены к PostgreSQL.
|
||
16. Storage facade делегирует записи и не владеет pool lifecycle.
|
||
17. Имена таблиц и колонок выбираются только из закрытого набора и
|
||
экранируются через `psycopg.sql.Identifier`.
|
||
18. Повторное создание месяца идемпотентно, а расхождение реестра и
|
||
PostgreSQL является явной ошибкой.
|
||
19. Неудачный setup partition откатывает создание, перенос и registry.
|
||
20. Retention требует отдельного `enabled` и положительного окна.
|
||
21. Выключенный retention не получает соединение и ничего не удаляет.
|
||
22. Ошибка очистки одного типа откатывает всю retention-операцию.
|
||
23. Runtime, Scheduler и существующие consumers не импортируют новый
|
||
Storage API.
|
||
24. Live и Recovery используют один persistence sink через общий
|
||
`TradeStreamConsistencyController`.
|
||
25. Validated Trade сохраняется до изменения checkpoint и dedup window.
|
||
26. Корректный дубликат сохраняется для provenance без продвижения
|
||
checkpoint.
|
||
27. Ordering error, conflicting duplicate и invalid Trade не передаются
|
||
в Storage.
|
||
28. Ошибка Storage не скрывается и не продвигает checkpoint.
|
||
29. Live persistence выполняется через owned `asyncio.to_thread` task
|
||
при закрытом live-processing gate.
|
||
30. Shutdown ждёт уже начатую live-запись и не оставляет её фоновой.
|
||
31. Отсутствующий optional sink сохраняет прежнее поведение Composition.
|
||
32. Bootstrap/settings не изменены, PostgreSQL pool не открывается.
|
||
33. Целевой набор и полная обычная регрессия проходят успешно.
|
||
34. Storage feature flag выключен по умолчанию и не строит зависимости.
|
||
35. Включённый Storage требует включённый Trade Stream.
|
||
36. Pool settings валидируются только при включённом Storage.
|
||
37. Bootstrap graph не открывает PostgreSQL и не запускает migrations.
|
||
38. Migration runner и Trade repository используют один pool identity.
|
||
39. Один observation sink передаётся общим Live/Recovery controller.
|
||
40. Pool открывается и migrations завершаются до root Runtime tasks.
|
||
41. Migration failure закрывает открытый pool и не запускает Runtime.
|
||
42. Blocking startup/shutdown выполняются вне asyncio event loop.
|
||
43. Cancellation ждёт завершения уже начатой blocking операции.
|
||
44. Trade Runtime полностью остановлен до закрытия pool.
|
||
45. Storage cleanup error не заменяет исходную Runtime ошибку.
|
||
46. Retention и partition scheduling автоматически не запускаются.
|
||
47. PostgreSQL integration требует точный opt-in flag и отдельный DSN.
|
||
48. Integration DSN принимает только localhost/local socket и database
|
||
name с префиксом `dzentra_test_`.
|
||
49. Harness не читает application `DB_*` и не управляет Docker скрытно.
|
||
50. Migrations 1–7 реально применяются в PostgreSQL и повторный запуск
|
||
ничего не меняет.
|
||
51. Два real migration runners применяют каждую версию ровно один раз.
|
||
52. Trades, Quotes и Candle revisions проходят real insert, duplicate,
|
||
provenance и conflict paths.
|
||
53. Trade batch реально откатывает предшествующую вставку при конфликте.
|
||
54. Два concurrent Trade writers сходятся к одной Canonical строке.
|
||
55. Partition DDL использует безопасные SQL literals вместо запрещённых
|
||
PostgreSQL parameters в `ALTER TABLE`.
|
||
56. Real partition manager переносит default rows и сериализует callers.
|
||
57. Real retention соблюдает точный cutoff и общий transaction rollback.
|
||
58. Live и reconnect/recovery Runtime сохраняют Trades в один repository.
|
||
59. Закрытый pool приводит к fatal Runtime error без продвижения
|
||
checkpoint и без записи проблемного Trade.
|
||
60. Application открывает и мигрирует Storage до Runtime, а закрывает pool
|
||
после Runtime; после shutdown нет owned tasks и pool connections.
|
||
61. Выключенный opt-in PostgreSQL-набор не требует локальной базы и
|
||
корректно пропускается обычной regression-командой.
|
||
62. Destructive cleanup непосредственно перед DDL повторно проверяет
|
||
точное имя disposable базы и специальный control application name.
|
||
63. Concurrent migration и partition tests подтверждают в PostgreSQL,
|
||
что обе caller-сессии действительно ожидают один advisory lock.
|
||
|
||
### 10.1. Opt-in PostgreSQL verification
|
||
|
||
Тесты никогда не используют production `DB_*`. Первый запуск требует
|
||
явного одноразового PostgreSQL и выполняется командой вида:
|
||
|
||
```bash
|
||
DZENTRA_RUN_POSTGRES_TESTS=1 \
|
||
DZENTRA_TEST_POSTGRES_DSN=postgresql://test:test@127.0.0.1:55432/dzentra_test_060_27 \
|
||
.venv/bin/python -m pytest -q -m integration \
|
||
tests/integration/market_data/storage
|
||
```
|
||
|
||
Harness проверяет имя базы и каждый local endpoint до соединения,
|
||
запрещает service/implicit fallback, берёт session advisory lock и
|
||
очищает только `market_data` и собственную таблицу migration history.
|
||
Перед каждым destructive cleanup он повторно подтверждает фактическое
|
||
имя disposable базы и identity контрольного соединения.
|
||
|
||
---
|
||
|
||
## 11. Финальная приёмка 060.27.8
|
||
|
||
Финальный этап выполнялся по verification-only правилу: production-код
|
||
не изменялся, потому что проверки не выявили нового дефекта.
|
||
|
||
Результаты на 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: 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 повторно проверил архитектурные зависимости,
|
||
SQL/DDL safety, durable-before-checkpoint ordering, fatal persistence
|
||
failure, lifecycle pool и Runtime, opt-in isolation и test coverage.
|
||
Новых findings не обнаружено.
|
||
|
||
## 12. Следующий Build
|
||
|
||
Build 060.27 завершён и принят. Следующий этап —
|
||
[Build 060.28](build_060_28_architecture.md), Persistent Checkpoint and
|
||
Startup Recovery.
|