perf(selection): 优化选股执行性能

This commit is contained in:
yuxuanhui
2026-08-12 09:45:16 +08:00
parent dd04933d63
commit 8963c067b3
22 changed files with 1333 additions and 120 deletions
@@ -1,5 +1,7 @@
"""HTTP presentation for persisted strategy execution results."""
import atexit
import threading
from datetime import date, datetime
from typing import Annotated, Literal
@@ -17,6 +19,7 @@ from zhixing_server.modules.selection.domain.runs import (
SelectionRunInProgress,
SelectionRunStoreError,
)
from zhixing_server.modules.selection.infrastructure.postgres_pool import SelectionPostgresPool
from zhixing_server.modules.selection.infrastructure.postgres_reader import (
PostgresMarketDataReader,
SelectionMarketDataNotReady,
@@ -27,6 +30,8 @@ from zhixing_server.modules.selection.infrastructure.postgres_runs import (
)
selection_router = APIRouter()
_SELECTION_POOL_CACHE_LOCK = threading.Lock()
_SELECTION_POOL_CACHE: dict[tuple[str, int], SelectionPostgresPool] = {}
StrategyValue = Literal["zhixing_b1"]
SelectionStatusValue = Literal[
@@ -117,11 +122,45 @@ class SelectionResultsResponse(BaseModel):
def get_selection_service(
settings: Annotated[Settings, Depends(get_settings)],
) -> RunZhixingB1:
"""Build one request-scoped selection application service."""
"""Build the selection service on top of process-scoped shared resources."""
reader = PostgresMarketDataReader(settings)
store = PostgresSelectionRunRepository(settings.database_url)
return RunZhixingB1(reader, store)
pool = get_selection_postgres_pool(settings)
reader = PostgresMarketDataReader(settings, pool=pool)
store = PostgresSelectionRunRepository(settings.database_url, pool=pool)
return RunZhixingB1(
reader,
store,
max_workers=settings.selection_max_workers,
batch_size=settings.selection_batch_size,
)
def get_selection_postgres_pool(settings: Settings) -> SelectionPostgresPool:
"""Return the cached bounded pool shared by selection adapters."""
key = (settings.database_url, settings.selection_max_workers + 2)
with _SELECTION_POOL_CACHE_LOCK:
pool = _SELECTION_POOL_CACHE.get(key)
if pool is None:
pool = SelectionPostgresPool(
settings.database_url,
max_connections=key[1],
)
_SELECTION_POOL_CACHE[key] = pool
return pool
def _close_cached_selection_pools() -> None:
"""Close all process-cached selection pools during interpreter shutdown."""
with _SELECTION_POOL_CACHE_LOCK:
pools = tuple(_SELECTION_POOL_CACHE.values())
_SELECTION_POOL_CACHE.clear()
for pool in pools:
pool.close()
atexit.register(_close_cached_selection_pools)
@selection_router.post(
@@ -292,5 +331,6 @@ __all__ = [
"SelectionRunAcceptedResponse",
"SelectionRunRequest",
"get_selection_service",
"get_selection_postgres_pool",
"selection_router",
]