From 86ac6ed0282c513e294f908a41c489262c1cae18 Mon Sep 17 00:00:00 2001 From: OG T Date: Wed, 8 Apr 2026 22:42:01 +0800 Subject: [PATCH] =?UTF-8?q?perf(api):=20HostAggregator=20=E6=95=88?= =?UTF-8?q?=E8=83=BD=E5=84=AA=E5=8C=96=20=E2=80=94=20probe=20timeout=20?= =?UTF-8?q?=E7=B8=AE=E7=9F=AD=20+=2030=20=E7=A7=92=E8=A8=98=E6=86=B6?= =?UTF-8?q?=E9=AB=94=E5=BF=AB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/api/src/services/host_aggregator.py | 31 +++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/apps/api/src/services/host_aggregator.py b/apps/api/src/services/host_aggregator.py index de922c65..f5301841 100644 --- a/apps/api/src/services/host_aggregator.py +++ b/apps/api/src/services/host_aggregator.py @@ -197,7 +197,7 @@ def get_baseline_context_for_llm(metrics: HostMetrics, host_name: str) -> str: # Real Host Probing # ============================================================================= -async def _tcp_probe(ip: str, port: int, timeout: float = 3.0) -> tuple[bool, float | None, str | None]: +async def _tcp_probe(ip: str, port: int, timeout: float = 1.5) -> tuple[bool, float | None, str | None]: """ Real TCP port probe using asyncio.open_connection @@ -236,7 +236,7 @@ async def _http_probe( ip: str, port: int, path: str, - timeout: float = 5.0, + timeout: float = 2.0, https: bool = False ) -> tuple[bool, float | None, str | None]: """ @@ -328,8 +328,18 @@ class HostAggregator: Uses asyncio.gather for parallel fetching of all host statuses. Performs real TCP/HTTP probes to determine service availability. + + 2026-04-08 Claude Code: Sprint 5 效能優化 + - TCP timeout: 3.0s → 1.5s + - HTTP timeout: 5.0s → 2.0s + - 記憶體快取: 上次成功結果 + 30 秒 TTL """ + # 記憶體快取 (避免 probe 超時時前端空白) + _cache: AggregatedStatus | None = None + _cache_time: float = 0 + _CACHE_TTL = 30 # 秒 + @classmethod async def _probe_service( cls, @@ -425,6 +435,7 @@ class HostAggregator: metrics=metrics, ) + @classmethod @classmethod async def fetch_all(cls) -> AggregatedStatus: """ @@ -432,7 +443,15 @@ class HostAggregator: Uses asyncio.gather for maximum concurrency. Always performs real probing - no mock data. + 快取: 30 秒內返回快取結果,避免前端等待超時 """ + import time as _time + + # 快取命中: 30 秒內直接回傳 + now = _time.monotonic() + if cls._cache is not None and (now - cls._cache_time) < cls._CACHE_TTL: + return cls._cache + logger.info("aggregator_fetch_start", mode="real_probing") # Fetch all hosts in parallel @@ -480,7 +499,7 @@ class HostAggregator: host_statuses={h.ip: h.status for h in hosts}, ) - return AggregatedStatus( + result = AggregatedStatus( timestamp=datetime.now(UTC), environment=settings.ENVIRONMENT, mock_mode=False, # Always real mode @@ -488,6 +507,12 @@ class HostAggregator: hosts=hosts, ) + # 更新快取 + cls._cache = result + cls._cache_time = _time.monotonic() + + return result + @classmethod async def fetch_single(cls, ip: str) -> HostStatus | None: """Fetch status from a single host"""