Build 060.30: finalize Market Data Acquisition documentation
This commit is contained in:
585
docs/architecture/trades_feed.md
Normal file
585
docs/architecture/trades_feed.md
Normal file
@@ -0,0 +1,585 @@
|
||||
# Trades Feed (Time & Sales) — текущая архитектура
|
||||
|
||||
**Статус:** Current; accepted in Build 060.30.1
|
||||
|
||||
**Область:** Production vertical slice Canonical Trades
|
||||
|
||||
**Версия документа:** 1.0
|
||||
|
||||
---
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- [Архитектура Build 060.30](../migrations/build_060_30_architecture.md)
|
||||
- [Эксплуатация Trade Stream Runtime](../operations/trades_feed_runtime.md)
|
||||
- [Build 060.27 — Persistent Market Data Storage](../migrations/build_060_27.md)
|
||||
- [Build 060.28 — Persistent Checkpoint](../migrations/build_060_28.md)
|
||||
- [Build 060.29 — Market Data Access and Replay](../migrations/build_060_29.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. Назначение и фактический scope
|
||||
|
||||
Trades Feed получает сделки Dzengi, преобразует их в Canonical `Trade`,
|
||||
проверяет порядок и дубликаты, восстанавливает разрывы через REST и при
|
||||
включённом Storage атомарно сохраняет сделку вместе с persistent
|
||||
checkpoint.
|
||||
|
||||
После перезапуска Runtime восстанавливает Consistency state из
|
||||
PostgreSQL, выполняет Startup Recovery и только затем пропускает
|
||||
накопленные live-сообщения. Сохранённые Canonical Market Data доступны
|
||||
через отдельные Historical Access и deterministic Replay API.
|
||||
|
||||
Краткая итоговая цепочка:
|
||||
|
||||
```text
|
||||
Dzengi WebSocket → Transport → validation/mapping → Canonical Trade ─┐
|
||||
├→ Consistency
|
||||
Dzengi REST Recovery → validation/mapping → Canonical Trade ─────────┘
|
||||
↓
|
||||
optional PostgreSQL Trade + checkpoint
|
||||
├→ next persistent startup
|
||||
│ → Hydration
|
||||
│ → Consistency state
|
||||
│ → Startup Recovery
|
||||
├→ explicit caller
|
||||
│ → Historical Access
|
||||
└→ explicit caller
|
||||
→ Replay Plan
|
||||
→ Replay Session → consumer
|
||||
```
|
||||
|
||||
Production ingestion и persistence в этой цепочке реализованы только
|
||||
для Trades. Наличие Quote/Candle моделей и repositories не означает,
|
||||
что Quotes и Candles уже подключены как Production feeds.
|
||||
|
||||
---
|
||||
|
||||
## 2. Матрица готовности Market Data
|
||||
|
||||
| Возможность | Trades | Quotes | Candle revisions |
|
||||
|---|---:|---:|---:|
|
||||
| Canonical model и Dzengi adapters | Да | Да | Да |
|
||||
| Production WebSocket Runtime consumer | Да | Нет | Нет |
|
||||
| Live/Recovery Consistency | Да | Нет | Нет |
|
||||
| Production persistence wiring | Да | Нет | Нет |
|
||||
| PostgreSQL repository и schema | Да | Да | Да |
|
||||
| Persistent operational checkpoint | Да | Нет | Нет |
|
||||
| Historical reader | Да | Да | Да |
|
||||
| Bounded deterministic Replay | Да | Да | Да |
|
||||
|
||||
Другие acquisition feeds проекта не входят в завершённую production
|
||||
вертикаль Trades Feed.
|
||||
|
||||
---
|
||||
|
||||
## 3. Режимы по feature flags
|
||||
|
||||
| Trade Stream | Market Data Storage | Фактическое поведение |
|
||||
|---|---|---|
|
||||
| выключен | выключен | Trade Runtime и Market Data pool не создаются |
|
||||
| включён | выключен | Live, in-memory Consistency и reconnect Recovery без restart persistence |
|
||||
| включён | включён | Durable Trades, checkpoint, Hydration и Startup Recovery |
|
||||
| выключен | включён | Недопустимая конфигурация; settings завершаются ошибкой |
|
||||
|
||||
Оба флага по умолчанию выключены. При включённом Trade Stream требуются
|
||||
явные WebSocket URL, REST base URL и symbols. Скрытых URL или symbol
|
||||
fallback нет.
|
||||
|
||||
Без Storage первоначальный startup подписывается без persistent
|
||||
Hydration, ожидания startup ACK и Startup REST Recovery. Reconnect
|
||||
Recovery во время уже работающего процесса остаётся доступным.
|
||||
|
||||
---
|
||||
|
||||
## 4. Карта компонентов
|
||||
|
||||
| Граница | Ответственность | Основные компоненты |
|
||||
|---|---|---|
|
||||
| Transport | WebSocket lifecycle, send/receive, Ping/Pong probe | `DzengiWebSocketTransport`, `WebSocketSession` |
|
||||
| Protocol | Команды, события, subscription и control routing | `AcquisitionRuntimeService`, `WebSocketSubscriptionManager` |
|
||||
| Adapter | JSON/schema/value validation и Canonical mapping | `DzengiUnifiedWebSocketAdapter`, REST adapter |
|
||||
| Runtime | Startup, receive loop, buffering, tasks и shutdown | `TradeStreamProductionRuntime` |
|
||||
| Consistency | Порядок, deduplication и per-symbol state | `TradeStreamConsistencyController`, `TradeStreamStateStore` |
|
||||
| Recovery | REST windows и повторная подача в Consistency | `RuntimeRecoveryCoordinator`, `TradeRecoveryController` |
|
||||
| Persistence | Canonical write, provenance и checkpoint | `TradeStorageObservationSink`, `PostgresTradeRepository` |
|
||||
| Startup | Hydration durable tail и Startup Recovery | `TradeStreamStateHydrator`, `RuntimeStartupRecoveryCoordinator` |
|
||||
| Read-side | Historical pages | `MarketDataHistoricalAccess`, PostgreSQL readers |
|
||||
| Replay | Snapshot, virtual clock и one-shot playback | `PostgresReplayPlanBuilder`, `ReplaySessionFactory`, `ReplaySession` |
|
||||
| Composition | Concrete dependency graph и root lifecycle | `bootstrap`, `ApplicationComposition` |
|
||||
|
||||
Transport не знает Canonical models, Recovery или PostgreSQL. Bootstrap
|
||||
является внешним composition root и создаёт concrete adapters.
|
||||
|
||||
---
|
||||
|
||||
## 5. Canonical Trade и Consistency
|
||||
|
||||
Один `TradeStreamState` принадлежит одному нормализованному symbol.
|
||||
Состояние хранит:
|
||||
|
||||
- последнюю принятую сделку;
|
||||
- in-memory checkpoint;
|
||||
- ограниченное окно Trade ID для deduplication;
|
||||
- Canonical payload уже наблюдавшихся сделок.
|
||||
|
||||
Размер deduplication tail по умолчанию равен 10 000 Trades.
|
||||
|
||||
Trade ID соответствует signed 32-bit контракту. Сравнение учитывает
|
||||
rollover:
|
||||
|
||||
- `INT32_MAX → INT32_MIN` является продвижением;
|
||||
- `-1 → 0` является продвижением;
|
||||
- расстояние ровно в половину 32-битного цикла неоднозначно и
|
||||
отклоняется.
|
||||
|
||||
Две доставки считаются одним биржевым фактом, если совпадают symbol,
|
||||
Trade ID, цена, количество, execution time и aggressor side. Поле
|
||||
`source` описывает путь доставки WebSocket/REST и не участвует в этом
|
||||
сравнении. При этом `source` сохраняется как часть Canonical payload и
|
||||
provenance.
|
||||
|
||||
Конфликтующий дубликат и нарушение rollover-aware порядка являются
|
||||
ошибками, а не молча отбрасываемыми данными.
|
||||
|
||||
Исходники:
|
||||
|
||||
- [TradeStreamState](../../app/src/market_data/acquisition/consistency/trade_stream_state.py)
|
||||
- [Consistency Controller](../../app/src/market_data/acquisition/consistency/trade_stream_consistency_controller.py)
|
||||
- [Trade ID sequence](../../app/src/market_data/acquisition/trade_id_sequence.py)
|
||||
|
||||
---
|
||||
|
||||
## 6. Live processing
|
||||
|
||||
Точная последовательность одного live-документа:
|
||||
|
||||
```text
|
||||
WebSocket receive
|
||||
→ зафиксировать transport activity
|
||||
→ опубликовать Runtime Event
|
||||
→ JSON decode
|
||||
→ войти в LiveProcessingGate
|
||||
→ разделить control и market document
|
||||
→ Dzengi adapter
|
||||
→ Canonical Trade
|
||||
→ общий Consistency Controller
|
||||
→ optional durable sink
|
||||
→ продвинуть in-memory state
|
||||
```
|
||||
|
||||
Blocking adapter/Consistency/Persistence path выполняется через
|
||||
`asyncio.to_thread`. Runtime владеет worker-задачей и при cancellation
|
||||
дожидается уже начатой обработки, чтобы не оставить неизвестный
|
||||
результат записи.
|
||||
|
||||
При выключенном Storage Consistency продвигает только память процесса.
|
||||
При включённом Storage durable callback завершается до изменения
|
||||
in-memory checkpoint.
|
||||
|
||||
Runtime events доставляются in-process последовательно. Publisher не
|
||||
имеет фоновой очереди или отдельной root task; production composition
|
||||
регистрирует logging consumer.
|
||||
|
||||
Исходники:
|
||||
|
||||
- [Production Runtime](../../app/src/market_data/acquisition/runtime/trade_stream_production_runtime.py)
|
||||
- [Acquisition Service](../../app/src/market_data/acquisition/trade_stream_acquisition_service.py)
|
||||
- [Runtime Event Publisher](../../app/src/market_data/acquisition/runtime/acquisition_runtime_event_publisher.py)
|
||||
|
||||
---
|
||||
|
||||
## 7. Durable write и persistent checkpoint
|
||||
|
||||
Для новой принятой сделки гарантия имеет следующий порядок:
|
||||
|
||||
```text
|
||||
BEGIN
|
||||
→ insert либо validate identical Trade
|
||||
→ compare-and-set persistent checkpoint
|
||||
→ COMMIT
|
||||
→ advance in-memory state
|
||||
```
|
||||
|
||||
Trade и checkpoint записываются одним `PostgresTradeRepository` и одной
|
||||
транзакцией. Ошибка вставки, payload conflict или checkpoint conflict
|
||||
откатывает операцию и не продвигает in-memory state.
|
||||
|
||||
Полный дубликат обрабатывается отдельно:
|
||||
|
||||
```text
|
||||
validate stored Trade
|
||||
→ optional provenance update
|
||||
→ persistent checkpoint не двигается
|
||||
→ in-memory checkpoint не двигается
|
||||
```
|
||||
|
||||
Идентичность строки Trade:
|
||||
|
||||
```text
|
||||
venue + symbol + trade_id + executed_at
|
||||
```
|
||||
|
||||
Persistent checkpoint имеет один ключ `venue + symbol` и ссылается на
|
||||
точную durable Trade identity через `DEFERRABLE INITIALLY DEFERRED`
|
||||
foreign key с `NO ACTION`.
|
||||
|
||||
Parent table Trades partitioned по диапазону `executed_at`; Quotes — по
|
||||
`received_at`, Candle revisions — по `open_time`. Миграции всегда
|
||||
создают default partition. Конкретная месячная UTC-partition появляется
|
||||
только после явного вызова Partition Manager. Symbol и venue являются
|
||||
identity/query scope, но не отдельными физическими уровнями
|
||||
partitioning.
|
||||
|
||||
Исходники:
|
||||
|
||||
- [Trade Storage Sink](../../app/src/market_data/storage/trade_storage_observation_sink.py)
|
||||
- [PostgreSQL Trade Repository](../../app/src/market_data/storage/postgres_trade_repository.py)
|
||||
- [Storage migrations](../../app/src/storage/migrations.py)
|
||||
|
||||
---
|
||||
|
||||
## 8. Startup с persistent state
|
||||
|
||||
Application сначала открывает Market Data pool и применяет migrations.
|
||||
Только после успешного Storage startup запускаются Telegram polling и
|
||||
Trade Runtime tasks.
|
||||
|
||||
Persistent Trade Runtime выполняет:
|
||||
|
||||
```text
|
||||
load checkpoint и bounded durable tail
|
||||
→ либо атомарно принять последнюю durable Trade при первом запуске
|
||||
→ опубликовать восстановленный StateStore целиком
|
||||
→ WebSocket connect
|
||||
→ отправить subscriptions
|
||||
→ дождаться соответствующего ACK
|
||||
→ сохранить ранние market documents в bounded FIFO
|
||||
→ REST Recovery до одной зафиксированной границы времени
|
||||
→ обработать FIFO в исходном порядке
|
||||
→ запустить Supervisor, receive loop и Scheduler
|
||||
```
|
||||
|
||||
Если для symbol нет checkpoint и durable Trades, state создаётся пустым;
|
||||
исторический backfill с биржи до начала доступного REST-окна не
|
||||
выполняется.
|
||||
|
||||
Если durable Trades есть, а checkpoint отсутствует, Hydrator может
|
||||
атомарно принять последнюю durable Trade как начальную точку. Corrupt,
|
||||
orphan или несовместимый checkpoint является фатальной ошибкой. Тихого
|
||||
сброса checkpoint нет.
|
||||
|
||||
Startup buffer по умолчанию ограничен 10 000 documents. Negative ACK,
|
||||
ACK timeout, overflow, Hydration error и Recovery error не открывают
|
||||
Live gate.
|
||||
|
||||
Исходники:
|
||||
|
||||
- [State Hydrator](../../app/src/market_data/acquisition/checkpoint/trade_stream_state_hydrator.py)
|
||||
- [Startup Recovery Coordinator](../../app/src/market_data/acquisition/runtime/runtime_startup_recovery_coordinator.py)
|
||||
- [Application lifecycle](../../app/src/bootstrap/application.py)
|
||||
|
||||
---
|
||||
|
||||
## 9. Reconnect и Recovery
|
||||
|
||||
Transport receive error и Heartbeat timeout используют одну
|
||||
generation-aware single-flight операцию:
|
||||
|
||||
```text
|
||||
закрыть общий Live gate
|
||||
→ Disconnect
|
||||
→ Connect
|
||||
→ переотправить desired subscriptions
|
||||
→ зафиксировать recovery_end_time
|
||||
→ последовательно выполнить REST Recovery для symbols
|
||||
→ тот же Consistency Controller и durable sink
|
||||
→ открыть Live gate
|
||||
```
|
||||
|
||||
Вторая причина reconnect для того же connection generation
|
||||
присоединяется к уже выполняющейся операции. Старое transport error не
|
||||
запускает новый reconnect после смены generation.
|
||||
|
||||
Важные границы:
|
||||
|
||||
- Reconnect выполняет одну попытку; retry loop и backoff отсутствуют.
|
||||
- Restore subscriptions означает успешную повторную отправку; отдельное
|
||||
ожидание ACK перед reconnect Recovery не реализовано.
|
||||
- Recovery использует REST `/api/v1/aggTrades` через настроенный base URL.
|
||||
- Окна ограничены `TRADE_STREAM_RECOVERY_WINDOW_MS`, по умолчанию
|
||||
3 599 999 ms.
|
||||
- Возможный повтор на границе окон удаляет общий Consistency layer.
|
||||
- Если state/checkpoint отсутствует, Recovery не придумывает начальную
|
||||
историю и возвращает пустой результат.
|
||||
- Ошибка reconnect или Recovery переводит gate в terminal failure и
|
||||
распространяется в Application.
|
||||
|
||||
Recovery гарантирует упорядоченную обработку фактически полученных REST
|
||||
данных, но не может гарантировать полноту данных, которых нет в ответе
|
||||
биржи.
|
||||
|
||||
Исходники:
|
||||
|
||||
- [Reconnect Coordinator](../../app/src/market_data/acquisition/runtime/reconnect.py)
|
||||
- [Reconnect Recovery Coordinator](../../app/src/market_data/acquisition/runtime/runtime_reconnect_recovery_coordinator.py)
|
||||
- [Runtime Recovery Coordinator](../../app/src/market_data/acquisition/runtime/runtime_recovery_coordinator.py)
|
||||
- [Trade Recovery Controller](../../app/src/market_data/acquisition/recovery/trade_recovery_controller.py)
|
||||
|
||||
---
|
||||
|
||||
## 10. Liveness, Heartbeat и Scheduler
|
||||
|
||||
Встроенный WebSocket keepalive библиотеки отключён. Источником
|
||||
transport liveness является явный Ping/Pong probe.
|
||||
|
||||
Scheduler периодически:
|
||||
|
||||
1. вызывает transport probe;
|
||||
2. при положительном ответе обновляет Heartbeat activity;
|
||||
3. проверяет Heartbeat timeout;
|
||||
4. передаёт подтверждённый timeout Supervisor.
|
||||
|
||||
Любое успешно полученное WebSocket-сообщение также считается activity.
|
||||
Отрицательный probe сам по себе не запускает reconnect немедленно:
|
||||
решение принимается по Heartbeat timeout. Supervisor не допускает
|
||||
параллельный второй timeout reconnect.
|
||||
|
||||
Scheduler не владеет своей asyncio task. Task принадлежит Production
|
||||
Runtime, который предварительно `claim()`-ит Scheduler и освобождает его
|
||||
после остановки.
|
||||
|
||||
Исходники:
|
||||
|
||||
- [Heartbeat](../../app/src/market_data/acquisition/runtime/heartbeat.py)
|
||||
- [Scheduler](../../app/src/market_data/acquisition/runtime/scheduler.py)
|
||||
- [Supervisor](../../app/src/market_data/acquisition/runtime/supervisor.py)
|
||||
|
||||
---
|
||||
|
||||
## 11. Historical Access
|
||||
|
||||
Historical Access является отдельной synchronous read-side границей и
|
||||
не расширяет write-only Storage facade.
|
||||
|
||||
Поддерживаются:
|
||||
|
||||
- Trade History;
|
||||
- Quote History;
|
||||
- Candle Revision History.
|
||||
|
||||
Общие свойства:
|
||||
|
||||
- timezone-aware полуоткрытый диапазон `[start_time, end_time)`;
|
||||
- forward-only keyset pagination;
|
||||
- строгий cursor scope;
|
||||
- Canonical validation прочитанных PostgreSQL rows;
|
||||
- limit по умолчанию 500, максимум 1 000.
|
||||
|
||||
Порядок страниц:
|
||||
|
||||
| Тип | Порядок |
|
||||
|---|---|
|
||||
| Trade | `executed_at`, затем `replay_sequence` |
|
||||
| Quote | `received_at`, затем `replay_sequence` |
|
||||
| Candle revision | `open_time`, затем `replay_sequence` |
|
||||
|
||||
Каждая страница выполняет отдельный запрос. Между страницами не
|
||||
удерживается одна `REPEATABLE READ` транзакция, поэтому concurrent writes
|
||||
или Retention могут изменить dataset следующего запроса.
|
||||
|
||||
Исходники:
|
||||
|
||||
- [Historical Access facade](../../app/src/market_data/access/market_data_historical_access.py)
|
||||
- [Historical models](../../app/src/market_data/access/models.py)
|
||||
|
||||
---
|
||||
|
||||
## 12. Deterministic Replay
|
||||
|
||||
Replay отделён от live Runtime:
|
||||
|
||||
```text
|
||||
caller
|
||||
→ ReplaySessionFactory.prepare_session()
|
||||
→ PostgresReplayPlanBuilder
|
||||
→ READ ONLY REPEATABLE READ snapshot
|
||||
→ bounded immutable ReplayPlan
|
||||
→ закрыть transaction и connection
|
||||
→ fresh Clock + fresh consumer + fresh ReplaySession
|
||||
→ caller await session.run()
|
||||
```
|
||||
|
||||
ReplayPlan ограничен максимум 100 000 events. Превышение limit приводит
|
||||
к явной ошибке без частичного плана.
|
||||
|
||||
Глобальный порядок задаётся парой:
|
||||
|
||||
```text
|
||||
replay_at + replay_sequence
|
||||
```
|
||||
|
||||
| Тип | `replay_at` |
|
||||
|---|---|
|
||||
| Trade | `executed_at` |
|
||||
| Quote | `received_at` |
|
||||
| Candle revision | `observed_at` |
|
||||
|
||||
`replay_sequence` выдаётся одним PostgreSQL sequence для всех трёх
|
||||
семейств и остаётся неизменяемым.
|
||||
|
||||
Clock начинается в `request.time_range.start_time`, разрешает равное
|
||||
время и запрещает движение назад. Session является one-shot:
|
||||
|
||||
```text
|
||||
CREATED → RUNNING → COMPLETED | FAILED | CANCELLED
|
||||
```
|
||||
|
||||
Повторный `run()` запрещён. Отдельного `ReplayEngine`, default consumer,
|
||||
background task, automatic startup и wall-clock pacing нет. Вызвавший
|
||||
код предоставляет `ReplayConsumerFactoryProtocol`. Затем
|
||||
`ReplaySessionFactory.prepare_session()` создаёт fresh consumer, Clock и
|
||||
Session; caller владеет полученной Session и её `run()`.
|
||||
|
||||
Исходники:
|
||||
|
||||
- [Replay Plan Builder](../../app/src/market_data/replay/postgres_replay_plan_builder.py)
|
||||
- [Replay Session Factory](../../app/src/market_data/replay/replay_session_factory.py)
|
||||
- [Replay Session](../../app/src/market_data/replay/replay_session.py)
|
||||
|
||||
---
|
||||
|
||||
## 13. Ownership и lifecycle
|
||||
|
||||
| Ресурс | Владелец |
|
||||
|---|---|
|
||||
| PostgreSQL pool и migrations | Application через `MarketDataStorageLifecycle` |
|
||||
| Telegram polling и root Runtime task | `run_application()` |
|
||||
| startup, receive, scheduler и market-processing tasks | `TradeStreamProductionRuntime` |
|
||||
| Heartbeat state | `RuntimeSupervisor` |
|
||||
| reconnect/recovery single-flight | `RuntimeReconnectRecoveryCoordinator` |
|
||||
| Hydration/Startup Recovery worker | Runtime через `RuntimeStartupRecoveryCoordinator` |
|
||||
| WebSocket connection | `DzengiWebSocketTransport` через Session |
|
||||
| Runtime Event delivery | caller `publish()`; фонового owner нет |
|
||||
| Historical connection/cursor | один repository call |
|
||||
| Replay snapshot transaction | `PostgresReplayPlanBuilder.create_plan()` |
|
||||
| Replay execution | caller `session.run()` |
|
||||
|
||||
Application shutdown:
|
||||
|
||||
```text
|
||||
отменить Telegram polling
|
||||
→ runtime.stop()
|
||||
→ завершить startup worker, если он выполняется
|
||||
→ остановить и дождаться Scheduler
|
||||
→ остановить Supervisor
|
||||
→ отменить и дождаться receive loop
|
||||
→ остановить WebSocket Session
|
||||
→ очистить subscriptions
|
||||
→ дождаться root Runtime task
|
||||
→ закрыть PostgreSQL pool
|
||||
→ закрыть bot HTTP session
|
||||
```
|
||||
|
||||
Ошибка включённого Runtime или persistent write считается фатальной для
|
||||
всего приложения. Degraded fallback к in-memory режиму не выполняется.
|
||||
|
||||
---
|
||||
|
||||
## 14. Направление зависимостей
|
||||
|
||||
```text
|
||||
bootstrap
|
||||
↓
|
||||
concrete Dzengi adapters + Runtime composition + PostgreSQL adapters
|
||||
|
||||
acquisition → Canonical models + own Protocol boundaries
|
||||
→ narrow Storage checkpoint contracts/exceptions
|
||||
Dzengi REST adapter → integrations.exchange REST client
|
||||
storage → Canonical models
|
||||
access → Canonical models + read contracts
|
||||
replay → Canonical models + access row materialization
|
||||
```
|
||||
|
||||
Подтверждённые границы:
|
||||
|
||||
- `market_data` не импортирует Telegram, Trading или Bootstrap;
|
||||
- Bootstrap импортирует concrete adapters как composition root;
|
||||
- Acquisition не зависит от Historical Access или Replay;
|
||||
- Production Runtime не импортирует concrete PostgreSQL repository;
|
||||
- Replay не зависит от Live Runtime и не вызывает Consistency;
|
||||
- Dzengi-specific код преимущественно находится в
|
||||
`acquisition/adapters/dzengi`, но provider subscription document и
|
||||
часть validation/handlers физически размещены в соседних acquisition
|
||||
пакетах.
|
||||
|
||||
Canonical models сейчас физически находятся в
|
||||
`market_data/acquisition/models`. Это фактическое расположение, а не
|
||||
утверждение, что общая Domain package уже выделена.
|
||||
|
||||
---
|
||||
|
||||
## 15. Failure policy
|
||||
|
||||
| Ошибка | Поведение |
|
||||
|---|---|
|
||||
| Неверные settings | Fail fast до запуска Runtime |
|
||||
| Storage pool или migration | Application startup завершается ошибкой |
|
||||
| Corrupt/orphan checkpoint | Startup завершается ошибкой |
|
||||
| Subscription ACK timeout/negative ACK при persistent startup | Live gate остаётся закрытым, Runtime завершается |
|
||||
| Startup buffer overflow | Runtime завершается |
|
||||
| WebSocket receive failure | Одна generation-aware reconnect/recovery попытка |
|
||||
| Reconnect или REST Recovery failure | Terminal gate failure и завершение Application |
|
||||
| Canonical conflict/order error | Не принимается и распространяется как ошибка |
|
||||
| Persistent write/checkpoint conflict | Transaction rollback, in-memory state не двигается |
|
||||
| Replay consumer failure | Session переходит в `FAILED` |
|
||||
| Replay cancellation | Session переходит в `CANCELLED` |
|
||||
|
||||
Ошибки не превращаются в молчаливое продолжение с потенциально
|
||||
несогласованным состоянием.
|
||||
|
||||
---
|
||||
|
||||
## 16. Доступный период и ограничения
|
||||
|
||||
Доступная история начинается не с момента существования инструмента на
|
||||
бирже, а с наиболее ранней Canonical записи, которая фактически
|
||||
находится в PostgreSQL. Обычно это момент успешного включения Storage,
|
||||
но история может также включать ранее импортированные durable rows.
|
||||
|
||||
Граница может сдвигаться из-за явно применённой Retention Policy.
|
||||
|
||||
Система не предоставляет:
|
||||
|
||||
- initial exchange historical backfill;
|
||||
- completeness metadata и доказательство полной биржевой истории;
|
||||
- storage raw exchange documents;
|
||||
- automatic monthly partition creation;
|
||||
- automatic Retention Scheduler;
|
||||
- Production persistence Quotes/Candles;
|
||||
- общий snapshot между Historical pages;
|
||||
- восстановление исходного порядка сетевых пакетов;
|
||||
- Replay HTTP/CLI/UI endpoint;
|
||||
- automatic Replay startup;
|
||||
- infinite reconnect retry/backoff;
|
||||
- HA, leader election, distributed lease или multi-instance ownership;
|
||||
- автоматические backup, metrics и alerting.
|
||||
|
||||
Default PostgreSQL partitions принимают строки без заранее созданной
|
||||
месячной partition. Явный Partition Manager позднее может атомарно
|
||||
перенести соответствующие строки.
|
||||
|
||||
Migration 9 выполняет блокирующий backfill `replay_sequence`. Для
|
||||
крупной базы нужны backup, измерение на сопоставимом объёме и отдельное
|
||||
maintenance window.
|
||||
|
||||
---
|
||||
|
||||
## 17. Безопасная итоговая формулировка
|
||||
|
||||
Trades Feed реализует единый Canonical Trade pipeline с live-получением,
|
||||
in-process consistency, generation-aware reconnect, REST gap recovery,
|
||||
опциональной durable-записью и persistent startup recovery. Historical
|
||||
Access и deterministic Replay работают над фактически сохранёнными
|
||||
Canonical Market Data. Полнота истории ограничена моментом включения
|
||||
persistence, доступностью REST Recovery и применяемой Retention Policy.
|
||||
Reference in New Issue
Block a user