Build 060.27: implement Persistent Market Data Storage
This commit is contained in:
@@ -1,13 +1,28 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from src.bootstrap.trade_stream_runtime import (
|
||||
build_trade_stream_production_runtime,
|
||||
)
|
||||
from src.core.config import Settings, TradeStreamSettings
|
||||
from src.core.config import (
|
||||
MarketDataStorageSettings,
|
||||
Settings,
|
||||
TradeStreamSettings,
|
||||
)
|
||||
from src.market_data.acquisition.runtime.trade_stream_production_runtime import (
|
||||
TradeStreamProductionRuntime,
|
||||
TradeStreamProductionRuntimeState,
|
||||
)
|
||||
from src.market_data.acquisition.models.trade import Trade
|
||||
|
||||
|
||||
class RecordingTradeObservationSink:
|
||||
def __init__(self) -> None:
|
||||
self.observations: list[Trade] = []
|
||||
|
||||
def persist(self, trade: Trade) -> None:
|
||||
self.observations.append(trade)
|
||||
|
||||
|
||||
def make_settings(
|
||||
@@ -49,6 +64,12 @@ def make_settings(
|
||||
db_name="test",
|
||||
db_user="test",
|
||||
db_password="test",
|
||||
market_data_storage=MarketDataStorageSettings(
|
||||
enabled=False,
|
||||
pool_min_size=1,
|
||||
pool_max_size=4,
|
||||
pool_timeout_seconds=10.0,
|
||||
),
|
||||
debug_enabled=False,
|
||||
journal_debug_enabled=False,
|
||||
)
|
||||
@@ -80,33 +101,60 @@ def test_uses_one_shared_stateful_dependency_graph() -> None:
|
||||
|
||||
assert isinstance(runtime, TradeStreamProductionRuntime)
|
||||
|
||||
transport = runtime._transport
|
||||
service = runtime._trade_stream_service
|
||||
reconnect_recovery = runtime._reconnect_recovery_coordinator
|
||||
runtime_graph: Any = runtime
|
||||
transport = runtime_graph._transport
|
||||
service = runtime_graph._trade_stream_service
|
||||
reconnect_recovery = runtime_graph._reconnect_recovery_coordinator
|
||||
recovery = reconnect_recovery._recovery_coordinator
|
||||
|
||||
assert runtime._session._transport is transport
|
||||
assert runtime._subscription_manager._transport is transport
|
||||
assert runtime._runtime_scheduler.liveness_probe is transport
|
||||
assert runtime_graph._session._transport is transport
|
||||
assert runtime_graph._subscription_manager._transport is transport
|
||||
assert runtime_graph._runtime_scheduler.liveness_probe is transport
|
||||
assert (
|
||||
service._consistency_controller
|
||||
is recovery._recovery_controller._consistency_controller
|
||||
)
|
||||
assert runtime._live_processing_gate is (
|
||||
assert runtime_graph._live_processing_gate is (
|
||||
reconnect_recovery.live_processing_gate
|
||||
)
|
||||
assert runtime._runtime_scheduler.runtime_supervisor is (
|
||||
runtime._runtime_supervisor
|
||||
assert runtime_graph._runtime_scheduler.runtime_supervisor is (
|
||||
runtime_graph._runtime_supervisor
|
||||
)
|
||||
|
||||
|
||||
def test_optional_storage_sink_is_shared_by_live_and_recovery() -> None:
|
||||
sink = RecordingTradeObservationSink()
|
||||
|
||||
runtime = build_trade_stream_production_runtime(
|
||||
make_settings(),
|
||||
trade_observation_sink=sink,
|
||||
)
|
||||
|
||||
assert isinstance(runtime, TradeStreamProductionRuntime)
|
||||
runtime_graph: Any = runtime
|
||||
live_controller = (
|
||||
runtime_graph._trade_stream_service._consistency_controller
|
||||
)
|
||||
recovery_controller = (
|
||||
runtime_graph
|
||||
._reconnect_recovery_coordinator
|
||||
._recovery_coordinator
|
||||
._recovery_controller
|
||||
._consistency_controller
|
||||
)
|
||||
|
||||
assert live_controller is recovery_controller
|
||||
assert live_controller._trade_observation_sink is sink
|
||||
|
||||
|
||||
def test_applies_explicit_transport_and_runtime_settings() -> None:
|
||||
settings = make_settings()
|
||||
|
||||
runtime = build_trade_stream_production_runtime(settings)
|
||||
|
||||
assert isinstance(runtime, TradeStreamProductionRuntime)
|
||||
transport = runtime._transport
|
||||
runtime_graph: Any = runtime
|
||||
transport = runtime_graph._transport
|
||||
|
||||
assert transport._url == "wss://stream.example.test/root/connect"
|
||||
assert transport._headers == {
|
||||
@@ -119,17 +167,17 @@ def test_applies_explicit_transport_and_runtime_settings() -> None:
|
||||
assert transport._close_timeout == 9.0
|
||||
assert transport._ping_interval is None
|
||||
assert transport._ping_timeout is None
|
||||
assert runtime._symbols == (
|
||||
assert runtime_graph._symbols == (
|
||||
"BTC/USD_LEVERAGE",
|
||||
"ETH/USD_LEVERAGE",
|
||||
)
|
||||
assert runtime._runtime_scheduler.interval_seconds == 6.0
|
||||
assert runtime_graph._runtime_scheduler.interval_seconds == 6.0
|
||||
assert (
|
||||
runtime._runtime_supervisor._heartbeat_monitor.timeout_seconds
|
||||
runtime_graph._runtime_supervisor._heartbeat_monitor.timeout_seconds
|
||||
== 31.0
|
||||
)
|
||||
assert (
|
||||
runtime._reconnect_recovery_coordinator
|
||||
runtime_graph._reconnect_recovery_coordinator
|
||||
._recovery_coordinator
|
||||
._window_planner
|
||||
.max_window_ms
|
||||
@@ -143,8 +191,9 @@ def test_recovery_rest_client_reuses_settings_snapshot() -> None:
|
||||
runtime = build_trade_stream_production_runtime(settings)
|
||||
|
||||
assert isinstance(runtime, TradeStreamProductionRuntime)
|
||||
runtime_graph: Any = runtime
|
||||
document_source = (
|
||||
runtime._reconnect_recovery_coordinator
|
||||
runtime_graph._reconnect_recovery_coordinator
|
||||
._recovery_coordinator
|
||||
._recovery_controller
|
||||
._document_source
|
||||
@@ -154,4 +203,4 @@ def test_recovery_rest_client_reuses_settings_snapshot() -> None:
|
||||
assert rest_client.settings is settings
|
||||
assert rest_client.base_url == "https://rest.example.test"
|
||||
assert rest_client.timeout == 17
|
||||
assert "X-MBX-APIKEY" not in runtime._transport._headers
|
||||
assert "X-MBX-APIKEY" not in runtime_graph._transport._headers
|
||||
|
||||
Reference in New Issue
Block a user