From ffaed4fc9d87ac2cfcdd4fa80b7156ee5bf059e6 Mon Sep 17 00:00:00 2001 From: I Luk Kim Date: Tue, 14 Apr 2026 16:35:15 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=84=9C=EB=B2=84=20=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=EB=B6=88=EB=8A=A5=20=E2=80=94=20limit-concurrency=2020=20+=20a?= =?UTF-8?q?syncio=20yield=20points?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BaseHTTPMiddleware는 요청당 2개 asyncio task를 생성한다. --limit-concurrency 100은 여전히 200개 task가 event loop를 포화시켜 health check 포함 모든 요청이 block됐다. 변경 사항: - docker-compose.yml: --limit-concurrency 100 → 20 (세마포어 10개 처리 + 10개 대기, 나머지 20초과 시 503으로 즉시 반환) - alpaca_price_service.py: upsert 루프 청크 사이 + Phase 4 ORM 일괄 생성 후 await asyncio.sleep(0) 추가 — 동기 CPU 블로킹 중 event loop yield 보장 Co-Authored-By: Claude Sonnet 4.6 --- app/services/alpaca_price_service.py | 4 ++++ docker-compose.yml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/services/alpaca_price_service.py b/app/services/alpaca_price_service.py index 7a4d24f..f187a69 100644 --- a/app/services/alpaca_price_service.py +++ b/app/services/alpaca_price_service.py @@ -2,6 +2,7 @@ Alpaca bars → AlpacaPriceData conversion and DB storage """ +import asyncio import logging from datetime import datetime, timezone from typing import Dict, List, Optional @@ -84,6 +85,7 @@ class AlpacaPriceService: stmt = stmt.on_conflict_do_nothing(constraint='uq_alpaca_price_data') result = await db.execute(stmt) inserted += result.rowcount + await asyncio.sleep(0) # yield event loop between chunks await db.commit() if inserted: @@ -183,6 +185,7 @@ class AlpacaPriceService: stmt = pg_insert(AlpacaPriceData).values(rows[i : i + CHUNK]) stmt = stmt.on_conflict_do_nothing(constraint="uq_alpaca_price_data") await db.execute(stmt) + await asyncio.sleep(0) # yield event loop between chunks await db.commit() logger.info( f"Alpaca multi-bars: stored {len(rows)} rows for {len(need_fetch)} tickers" @@ -203,6 +206,7 @@ class AlpacaPriceService: .order_by(AlpacaPriceData.ticker, AlpacaPriceData.date) ) db_rows = result.scalars().all() + await asyncio.sleep(0) # yield after bulk ORM object creation data: Dict[str, List[AlpacaPriceData]] = {t: [] for t in upper_tickers} for row in db_rows: diff --git a/docker-compose.yml b/docker-compose.yml index 144b6b8..626ca19 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -66,7 +66,7 @@ services: - ./yfinance_plus:/app/yfinance_plus # Mount yfinance_plus for development - ./data:/app/data # For data files restart: unless-stopped - command: ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "18000", "--reload", "--limit-concurrency", "100"] + command: ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "18000", "--reload", "--limit-concurrency", "20"] # Frontend Application frontend: