Twenty-first pass: no ABI compatibility check on Rust .so load stale binary
corrupts silently (X1 Critical), real_zinc_plane _write_region zeroes entire
buffer before write visible all-zero window (X2 Critical), no requirements.txt
setup.py pyproject.toml zero Python dependency declarations (X3 Critical),
RealZincControlPlane.update() no thread lock concurrent calls corrupt seq and
shared memory (X4 High), libc declared in Cargo.toml never used dead dependency
(X5 High), 5 test files hardcoded sys.path.insert non-portable (X6 High),
_decode_packet no try/except on json.loads partial body read crashes reader (X7
High), ExchangeEvent not exported from __init__.py package API inconsistency (X8
High), RealZincPlane and RealZincControlPlane collide on {prefix}_control region
name (X10 Medium). 375 total flaws across 21 passes.
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
362 lines
13 KiB
Python
362 lines
13 KiB
Python
"""Operator-facing bootstrap helpers for DITAv2.
|
|
|
|
This module keeps the wiring explicit:
|
|
- control plane selection
|
|
- Zinc plane selection
|
|
- projection sink selection
|
|
- venue adapter selection
|
|
|
|
The defaults stay safe and testable. Real shared-memory or live BingX wiring
|
|
is only enabled when the caller opts in via arguments or environment.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from enum import Enum
|
|
import asyncio
|
|
import inspect
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Any, Optional
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
from prod.bingx.config import BingxExecClientConfig
|
|
from prod.bingx.config import BingxInstrumentProviderConfig
|
|
from prod.bingx.enums import BingxEnvironment
|
|
|
|
from .bingx_venue import BingxVenueAdapter
|
|
from .control import BackendMode
|
|
from .control import ControlPlane
|
|
from .control import ControlUpdate
|
|
from .control import KernelControlSnapshot
|
|
from .control import KernelMode
|
|
from .control import KernelVerbosity
|
|
from .control import build_control_plane
|
|
from .mock_venue import MockVenueAdapter
|
|
from .mock_venue import MockVenueScenario
|
|
from .projection import HazelcastProjection
|
|
from .projection import build_projection
|
|
from .real_control_plane import RealZincControlPlane
|
|
from .real_control_plane import RealZincUnavailable
|
|
from .real_zinc_plane import RealZincPlane
|
|
from .real_zinc_plane import RealZincUnavailable as RealZincPlaneUnavailable
|
|
from .rust_backend import ExecutionKernel
|
|
from .venue import VenueAdapter
|
|
from .zinc_plane import InMemoryZincPlane
|
|
from .zinc_plane import ZincPlane
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[3]
|
|
load_dotenv(PROJECT_ROOT / ".env")
|
|
|
|
|
|
class LauncherVenueMode(str, Enum):
|
|
MOCK = "MOCK"
|
|
BINGX = "BINGX"
|
|
|
|
|
|
class LauncherZincMode(str, Enum):
|
|
IN_MEMORY = "IN_MEMORY"
|
|
REAL = "REAL"
|
|
|
|
|
|
@dataclass
|
|
class DITAv2LauncherBundle:
|
|
"""Concrete runtime components assembled by the launcher."""
|
|
|
|
kernel: ExecutionKernel
|
|
control_plane: ControlPlane
|
|
projection: HazelcastProjection
|
|
zinc_plane: ZincPlane
|
|
venue: VenueAdapter
|
|
|
|
def close(self) -> None:
|
|
_maybe_close(self.venue)
|
|
_maybe_close(self.zinc_plane)
|
|
_maybe_close(self.control_plane)
|
|
# V1: kernel.close() was added (O10) but never wired into bundle teardown.
|
|
self.kernel.close()
|
|
|
|
|
|
def _env_upper(name: str, default: str = "") -> str:
|
|
return str(os.environ.get(name, default)).strip().upper()
|
|
|
|
|
|
def _env_bool(name: str, default: bool = False) -> bool:
|
|
raw = os.environ.get(name)
|
|
if raw is None:
|
|
return default
|
|
return str(raw).strip().lower() in {"1", "true", "yes", "on"}
|
|
|
|
|
|
def _resolve_control_mode() -> KernelMode | None:
|
|
raw = _env_upper("DITA_V2_MODE", "")
|
|
if raw == KernelMode.DEBUG.value:
|
|
return KernelMode.DEBUG
|
|
if raw == KernelMode.NORMAL.value:
|
|
return KernelMode.NORMAL
|
|
return None
|
|
|
|
|
|
def _resolve_control_verbosity() -> KernelVerbosity | None:
|
|
raw = _env_upper("DITA_V2_VERBOSITY", "")
|
|
if raw == KernelVerbosity.TRACE.value:
|
|
return KernelVerbosity.TRACE
|
|
if raw == KernelVerbosity.VERBOSE.value:
|
|
return KernelVerbosity.VERBOSE
|
|
if raw == KernelVerbosity.QUIET.value:
|
|
return KernelVerbosity.QUIET
|
|
return None
|
|
|
|
|
|
def _resolve_backend_mode() -> BackendMode | None:
|
|
raw = _env_upper("DITA_V2_BACKEND_MODE", "")
|
|
if raw == BackendMode.BINGX.value:
|
|
return BackendMode.BINGX
|
|
if raw == BackendMode.MOCK.value:
|
|
return BackendMode.MOCK
|
|
return None
|
|
|
|
|
|
def _control_update_from_env() -> ControlUpdate | None:
|
|
fields: dict[str, Any] = {}
|
|
mode = _resolve_control_mode()
|
|
if mode is not None:
|
|
fields["mode"] = mode
|
|
verbosity = _resolve_control_verbosity()
|
|
if verbosity is not None:
|
|
fields["verbosity"] = verbosity
|
|
backend_mode = _resolve_backend_mode()
|
|
if backend_mode is not None:
|
|
fields["backend_mode"] = backend_mode
|
|
raw = os.environ.get("DITA_V2_DEBUG_CLICKHOUSE")
|
|
if raw is not None:
|
|
fields["debug_clickhouse_enabled"] = _env_bool("DITA_V2_DEBUG_CLICKHOUSE", True)
|
|
raw = os.environ.get("DITA_V2_TRACE_TRANSITIONS")
|
|
if raw is not None:
|
|
fields["trace_transitions"] = _env_bool("DITA_V2_TRACE_TRANSITIONS", False)
|
|
raw = os.environ.get("DITA_V2_MIRROR_TO_HAZELCAST")
|
|
if raw is not None:
|
|
fields["mirror_to_hazelcast"] = _env_bool("DITA_V2_MIRROR_TO_HAZELCAST", True)
|
|
raw = os.environ.get("DITA_V2_ACTIVE_SLOT_LIMIT")
|
|
if raw is not None:
|
|
try:
|
|
fields["active_slot_limit"] = max(1, int(str(raw).strip()))
|
|
except Exception:
|
|
pass
|
|
raw = os.environ.get("DITA_V2_RECONCILE_ON_RESTART")
|
|
if raw is not None:
|
|
fields["reconcile_on_restart"] = _env_bool("DITA_V2_RECONCILE_ON_RESTART", True)
|
|
return ControlUpdate(**fields) if fields else None
|
|
|
|
|
|
def _resolve_venue_mode(venue_mode: Optional[str] = None) -> LauncherVenueMode:
|
|
raw = _env_upper("DITA_V2_VENUE", venue_mode or LauncherVenueMode.MOCK.value)
|
|
if raw == LauncherVenueMode.BINGX.value:
|
|
return LauncherVenueMode.BINGX
|
|
return LauncherVenueMode.MOCK
|
|
|
|
|
|
def _resolve_zinc_mode(zinc_mode: Optional[str] = None) -> LauncherZincMode:
|
|
raw = _env_upper("DITA_V2_ZINC", zinc_mode or LauncherZincMode.IN_MEMORY.value)
|
|
if raw == LauncherZincMode.REAL.value:
|
|
return LauncherZincMode.REAL
|
|
return LauncherZincMode.IN_MEMORY
|
|
|
|
|
|
def _resolve_hazelcast_real(prefer_real_hazelcast: Optional[bool] = None) -> bool:
|
|
if prefer_real_hazelcast is not None:
|
|
return bool(prefer_real_hazelcast)
|
|
raw = _env_upper("DITA_V2_HAZELCAST", "")
|
|
return raw in {"REAL", "REAL_HZ", "HAZELCAST"}
|
|
|
|
|
|
def build_bingx_exec_client_config(
|
|
*,
|
|
environment: Optional[BingxEnvironment] = None,
|
|
allow_mainnet: Optional[bool] = None,
|
|
recv_window_ms: Optional[int] = None,
|
|
default_leverage: Optional[int] = None,
|
|
exchange_leverage_cap: Optional[int] = None,
|
|
prefer_websocket: Optional[bool] = None,
|
|
sizing_mode: Optional[str] = None,
|
|
) -> BingxExecClientConfig:
|
|
"""Build the direct BingX config used by the DITAv2 launcher."""
|
|
|
|
resolved_environment = environment or (
|
|
BingxEnvironment.LIVE if _env_upper("DOLPHIN_BINGX_ENV", "VST") == "LIVE" else BingxEnvironment.VST
|
|
)
|
|
resolved_allow_mainnet = _env_bool("DOLPHIN_BINGX_ALLOW_MAINNET", False) if allow_mainnet is None else bool(allow_mainnet)
|
|
resolved_recv_window = int(os.environ.get("DOLPHIN_BINGX_RECV_WINDOW_MS", "5000")) if recv_window_ms is None else int(recv_window_ms)
|
|
resolved_default_leverage = int(os.environ.get("DOLPHIN_BINGX_DEFAULT_LEVERAGE", "1")) if default_leverage is None else int(default_leverage)
|
|
resolved_exchange_cap = int(os.environ.get("DOLPHIN_BINGX_EXCHANGE_LEVERAGE_CAP", "3")) if exchange_leverage_cap is None else int(exchange_leverage_cap)
|
|
resolved_prefer_ws = _env_bool("DOLPHIN_BINGX_PREFER_WEBSOCKET", False) if prefer_websocket is None else bool(prefer_websocket)
|
|
resolved_sizing_mode = sizing_mode or os.environ.get("DOLPHIN_BINGX_SIZING_MODE", "testnet")
|
|
return BingxExecClientConfig(
|
|
api_key=os.environ.get("BINGX_API_KEY"),
|
|
secret_key=os.environ.get("BINGX_SECRET_KEY"),
|
|
environment=resolved_environment,
|
|
allow_mainnet=resolved_allow_mainnet,
|
|
recv_window_ms=max(1, resolved_recv_window),
|
|
default_leverage=max(1, resolved_default_leverage),
|
|
exchange_leverage_cap=max(1, resolved_exchange_cap),
|
|
prefer_websocket=resolved_prefer_ws,
|
|
sizing_mode=resolved_sizing_mode,
|
|
journal_strategy=os.environ.get("DOLPHIN_BINGX_JOURNAL_STRATEGY", "dita_v2"),
|
|
journal_db=os.environ.get("DOLPHIN_BINGX_JOURNAL_DB", "dolphin_pink"),
|
|
instrument_provider=BingxInstrumentProviderConfig(load_all=True),
|
|
)
|
|
|
|
|
|
def _build_control_plane(
|
|
*,
|
|
prefix: str,
|
|
control_plane: Optional[ControlPlane] = None,
|
|
) -> ControlPlane:
|
|
plane = control_plane or build_control_plane(prefix=prefix)
|
|
update = _control_update_from_env()
|
|
if update is not None:
|
|
plane.update(update)
|
|
return plane
|
|
|
|
|
|
def _build_zinc_plane(
|
|
*,
|
|
prefix: str,
|
|
slot_count: int,
|
|
zinc_mode: Optional[LauncherZincMode] = None,
|
|
zinc_plane: Optional[ZincPlane] = None,
|
|
) -> ZincPlane:
|
|
if zinc_plane is not None:
|
|
return zinc_plane
|
|
resolved_mode = zinc_mode or _resolve_zinc_mode()
|
|
if resolved_mode is LauncherZincMode.REAL:
|
|
try:
|
|
return RealZincPlane(prefix=prefix, slot_count=slot_count, create=True)
|
|
except (RealZincPlaneUnavailable, RealZincUnavailable, Exception):
|
|
pass
|
|
return InMemoryZincPlane()
|
|
|
|
|
|
def _build_venue(
|
|
*,
|
|
venue_mode: Optional[LauncherVenueMode] = None,
|
|
mock_scenario: Optional[MockVenueScenario] = None,
|
|
bingx_config: Optional[BingxExecClientConfig] = None,
|
|
bingx_backend: Optional[Any] = None,
|
|
venue: Optional[VenueAdapter] = None,
|
|
) -> VenueAdapter:
|
|
if venue is not None:
|
|
return venue
|
|
resolved_mode = venue_mode or _resolve_venue_mode()
|
|
if resolved_mode is LauncherVenueMode.BINGX:
|
|
backend = bingx_backend
|
|
if backend is None:
|
|
from prod.clean_arch.adapters.bingx_direct import BingxDirectExecutionAdapter
|
|
|
|
backend = BingxDirectExecutionAdapter(bingx_config or build_bingx_exec_client_config())
|
|
return BingxVenueAdapter(backend=backend)
|
|
return MockVenueAdapter(mock_scenario)
|
|
|
|
|
|
def _maybe_close(obj: Any) -> None:
|
|
for method_name in ("close", "disconnect"):
|
|
method = getattr(obj, method_name, None)
|
|
if method is None:
|
|
continue
|
|
try:
|
|
result = method()
|
|
except TypeError:
|
|
continue
|
|
if inspect.isawaitable(result):
|
|
try:
|
|
loop = asyncio.get_running_loop()
|
|
except RuntimeError:
|
|
loop = None
|
|
if loop is not None and loop.is_running():
|
|
# Called from within an async context — schedule on the
|
|
# shared executor so asyncio.run() can create its own loop
|
|
# without conflicting with the caller's loop (O1).
|
|
import concurrent.futures as _cf
|
|
with _cf.ThreadPoolExecutor(max_workers=1) as _pool:
|
|
_pool.submit(asyncio.run, result).result(timeout=10.0)
|
|
else:
|
|
asyncio.run(result)
|
|
break
|
|
|
|
|
|
def build_launcher_bundle(
|
|
*,
|
|
max_slots: int = 10,
|
|
prefix: Optional[str] = None,
|
|
control_plane: Optional[ControlPlane] = None,
|
|
projection: Optional[HazelcastProjection] = None,
|
|
projection_client: Optional[Any] = None,
|
|
zinc_plane: Optional[ZincPlane] = None,
|
|
venue: Optional[VenueAdapter] = None,
|
|
venue_mode: Optional[LauncherVenueMode | str] = None,
|
|
zinc_mode: Optional[LauncherZincMode | str] = None,
|
|
bingx_config: Optional[BingxExecClientConfig] = None,
|
|
bingx_backend: Optional[Any] = None,
|
|
mock_scenario: Optional[MockVenueScenario] = None,
|
|
) -> DITAv2LauncherBundle:
|
|
"""Build a fully wired DITAv2 runtime bundle.
|
|
|
|
Defaults stay non-destructive:
|
|
- in-memory Zinc plane
|
|
- in-process control plane
|
|
- mock venue
|
|
- callback projection unless a Hazelcast client is supplied
|
|
"""
|
|
|
|
resolved_prefix = (prefix or os.environ.get("DITA_V2_PREFIX", "dita_v2")).strip() or "dita_v2"
|
|
if isinstance(venue_mode, LauncherVenueMode):
|
|
resolved_venue_mode = venue_mode
|
|
elif isinstance(venue_mode, str):
|
|
resolved_venue_mode = LauncherVenueMode(venue_mode.strip().upper())
|
|
else:
|
|
resolved_venue_mode = None
|
|
if isinstance(zinc_mode, LauncherZincMode):
|
|
resolved_zinc_mode = zinc_mode
|
|
elif isinstance(zinc_mode, str):
|
|
resolved_zinc_mode = LauncherZincMode(zinc_mode.strip().upper())
|
|
else:
|
|
resolved_zinc_mode = None
|
|
|
|
active_control_plane = _build_control_plane(prefix=resolved_prefix, control_plane=control_plane)
|
|
control_snapshot = active_control_plane.read()
|
|
active_projection = projection or build_projection(
|
|
client=projection_client,
|
|
prefer_real_hazelcast=_resolve_hazelcast_real(),
|
|
control_snapshot=control_snapshot,
|
|
)
|
|
active_zinc_plane = _build_zinc_plane(
|
|
prefix=resolved_prefix,
|
|
slot_count=int(max_slots),
|
|
zinc_mode=resolved_zinc_mode,
|
|
zinc_plane=zinc_plane,
|
|
)
|
|
active_venue = _build_venue(
|
|
venue_mode=resolved_venue_mode,
|
|
mock_scenario=mock_scenario,
|
|
bingx_config=bingx_config,
|
|
bingx_backend=bingx_backend,
|
|
venue=venue,
|
|
)
|
|
kernel = ExecutionKernel(
|
|
max_slots=int(max_slots),
|
|
control_plane=active_control_plane,
|
|
venue=active_venue,
|
|
projection=active_projection,
|
|
projection_client=projection_client,
|
|
zinc_plane=active_zinc_plane,
|
|
)
|
|
return DITAv2LauncherBundle(
|
|
kernel=kernel,
|
|
control_plane=active_control_plane,
|
|
projection=active_projection,
|
|
zinc_plane=active_zinc_plane,
|
|
venue=active_venue,
|
|
)
|