diff --git a/app/api/v1/endpoints/alpaca.py b/app/api/v1/endpoints/alpaca.py index 1e7e115..3d91bd3 100644 --- a/app/api/v1/endpoints/alpaca.py +++ b/app/api/v1/endpoints/alpaca.py @@ -128,26 +128,37 @@ async def get_alpaca_intraday_multi( start_dt = datetime.combine(_start, datetime.min.time()).replace(tzinfo=timezone.utc) end_dt = datetime.combine(_end, datetime.max.time()).replace(tzinfo=timezone.utc) - async with _INTRADAY_SEMAPHORE: - try: - data = await svc.get_or_fetch_multi_bars( - symbols, start_dt, end_dt, interval, force_refresh, feed="sip" - ) - except Exception as e: - err = str(e) - detail = f"Alpaca API error: {err}" - if "502" in err or "Bad Gateway" in err: - detail = ( - f"Alpaca 502 Bad Gateway — 요청당 심볼 수 초과 가능성. " - f"내부 배치 크기: 100개/요청. 원인: {err}" + async def _do_fetch(): + async with _INTRADAY_SEMAPHORE: + try: + return await svc.get_or_fetch_multi_bars( + symbols, start_dt, end_dt, interval, force_refresh, feed="sip" ) - raise HTTPException(status_code=502, detail=detail) - finally: - await svc.client.close() + finally: + try: + await svc.client.close() + except Exception: + pass + + try: + data = await asyncio.wait_for(_do_fetch(), timeout=120) + except asyncio.TimeoutError: + raise HTTPException( + status_code=504, + detail="요청 시간 초과 (120초). 티커 수를 줄이거나 나중에 다시 시도하세요.", + ) + except HTTPException: + raise + except Exception as e: + err = str(e) + detail = f"Alpaca API error: {err}" + if "502" in err or "Bad Gateway" in err: + detail = ( + f"Alpaca 502 Bad Gateway — 요청당 심볼 수 초과 가능성. " + f"내부 배치 크기: 100개/요청. 원인: {err}" + ) + raise HTTPException(status_code=502, detail=detail) - # Reclaim Alpaca HTTP buffers, intermediate bar dicts, and DB row objects - # before building the serialised response. Prevents Python heap from growing - # unboundedly across thousands of backfill requests in a long-running worker. gc.collect() bars = { @@ -207,22 +218,36 @@ async def get_alpaca_intraday_today( start_dt = datetime.combine(today, datetime.min.time()).replace(tzinfo=timezone.utc) end_dt = datetime.combine(today, datetime.max.time()).replace(tzinfo=timezone.utc) - async with _INTRADAY_SEMAPHORE: - try: - data = await svc.get_or_fetch_multi_bars( - symbols, start_dt, end_dt, interval, force_refresh=True, feed="iex" - ) - except Exception as e: - err = str(e) - detail = f"Alpaca API error: {err}" - if "502" in err or "Bad Gateway" in err: - detail = ( - f"Alpaca 502 Bad Gateway — 요청당 심볼 수 초과 가능성. " - f"내부 배치 크기: 100개/요청. 원인: {err}" + async def _do_fetch(): + async with _INTRADAY_SEMAPHORE: + try: + return await svc.get_or_fetch_multi_bars( + symbols, start_dt, end_dt, interval, force_refresh=True, feed="iex" ) - raise HTTPException(status_code=502, detail=detail) - finally: - await svc.client.close() + finally: + try: + await svc.client.close() + except Exception: + pass + + try: + data = await asyncio.wait_for(_do_fetch(), timeout=120) + except asyncio.TimeoutError: + raise HTTPException( + status_code=504, + detail="요청 시간 초과 (120초). 티커 수를 줄이거나 나중에 다시 시도하세요.", + ) + except HTTPException: + raise + except Exception as e: + err = str(e) + detail = f"Alpaca API error: {err}" + if "502" in err or "Bad Gateway" in err: + detail = ( + f"Alpaca 502 Bad Gateway — 요청당 심볼 수 초과 가능성. " + f"내부 배치 크기: 100개/요청. 원인: {err}" + ) + raise HTTPException(status_code=502, detail=detail) gc.collect() diff --git a/docker-compose.yml b/docker-compose.yml index a5d3687..606ef50 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -68,7 +68,7 @@ services: mem_limit: 2g memswap_limit: 2g restart: unless-stopped - command: ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "18000", "--reload", "--limit-concurrency", "20"] + command: ["python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "18000", "--reload", "--limit-concurrency", "50"] # Frontend Application frontend: