Skip to content

Commit 467cc25

Browse files
authored
Merge pull request #443 from easygap/fix/dashboard-nonblocking
fix: 대시보드 첫 로드 블로킹 해소 — 동기 조회를 스레드로 격리
2 parents 491fe2e + 824545c commit 467cc25

1 file changed

Lines changed: 26 additions & 11 deletions

File tree

monitoring/web_dashboard.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -754,8 +754,11 @@ async def handle_index(_request: web.Request) -> web.Response:
754754

755755

756756
async def handle_api_portfolio(_request: web.Request) -> web.Response:
757+
# live 모드에서는 KIS 잔고 조회(동기 네트워크)가 섞일 수 있다 — 스레드로 격리.
758+
import asyncio
759+
757760
try:
758-
data = get_portfolio_json()
761+
data = await asyncio.to_thread(get_portfolio_json)
759762
return web.json_response(data)
760763
except Exception as e:
761764
logger.exception("API /api/portfolio 오류: {}", e)
@@ -819,8 +822,14 @@ async def handle_api_cash_flows(request: web.Request) -> web.Response:
819822

820823

821824
async def handle_api_runtime(_request: web.Request) -> web.Response:
825+
# get_runtime_json은 시장 국면 실시간 조회(동기 네트워크, 수십 초 가능)를 포함한다 —
826+
# 이벤트 루프에서 직접 부르면 그동안 '/'·내 자산·차트까지 전부 멈춘다(첫 로드 체감 저하).
827+
# 스레드로 내려 다른 엔드포인트는 즉시 응답하게 한다.
828+
import asyncio
829+
822830
try:
823-
return web.json_response(get_runtime_json())
831+
data = await asyncio.to_thread(get_runtime_json)
832+
return web.json_response(data)
824833
except Exception as e:
825834
logger.exception("API /api/runtime 오류: {}", e)
826835
return web.json_response({"error": str(e)}, status=500)
@@ -851,16 +860,10 @@ async def handle_api_basket_evaluation(_request: web.Request) -> web.Response:
851860
read-only. include_benchmark=False로 네트워크(KS11 조회)를 피한다 — 대시보드는
852861
10초 폴링이므로 외부 조회를 섞으면 안 된다. 결과는 60초 TTL 캐시.
853862
"""
863+
import asyncio
854864
import time as _time
855865

856-
try:
857-
now = _time.monotonic()
858-
if (
859-
_BASKET_EVAL_CACHE["data"] is not None
860-
and now - _BASKET_EVAL_CACHE["at"] < _BASKET_EVAL_TTL_SEC
861-
):
862-
return web.json_response(_BASKET_EVAL_CACHE["data"])
863-
866+
def _collect_all() -> dict:
864867
from core.basket_evaluation import collect_basket_paper_evaluation
865868
from core.basket_rebalancer import BasketRebalancer
866869

@@ -877,7 +880,19 @@ async def handle_api_basket_evaluation(_request: web.Request) -> web.Response:
877880
"snapshot_coverage": result.get("snapshot_coverage"),
878881
"issues": result.get("issues", []),
879882
})
880-
payload = {"evaluations": out}
883+
return {"evaluations": out}
884+
885+
try:
886+
now = _time.monotonic()
887+
if (
888+
_BASKET_EVAL_CACHE["data"] is not None
889+
and now - _BASKET_EVAL_CACHE["at"] < _BASKET_EVAL_TTL_SEC
890+
):
891+
return web.json_response(_BASKET_EVAL_CACHE["data"])
892+
893+
# 수집기는 TradingHours 초기화·(잠재) pykrx 갱신 등 동기 작업 — 스레드로 내려
894+
# 캐시 미스 시에도 이벤트 루프가 다른 요청을 계속 처리하게 한다.
895+
payload = await asyncio.to_thread(_collect_all)
881896
_BASKET_EVAL_CACHE["at"] = now
882897
_BASKET_EVAL_CACHE["data"] = payload
883898
return web.json_response(payload)

0 commit comments

Comments
 (0)