Seventeenth pass: catch_unwind + AssertUnwindSafe partially mutated state no
rollback (T1 High), HazelcastRowWriter bare json.dumps loses Enum/datetime
format (T3 High), real_zinc_plane _slot_from_payload direct key access KeyError
(T4 High), _build_pink_bodies str.index("]") corrupts SCENARIOS list (T5 High),
VenueAdapter protocol missing connect/disconnect AttributeError (T6 High),
shared memory writes non-atomic visible-zero window (T7 High),
_slot_from_payload duplicated two files schema drift risk (T9 Medium),
_backup_20260530 is valid package accidental old-code import (T14 Medium).
319 total flaws across 17 passes.
Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
360 lines
13 KiB
Python
360 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)
|
|
|
|
|
|
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,
|
|
)
|