유튜브 네트워크 오류 재시도 설정 추가

This commit is contained in:
김성경 2026-07-28 16:07:08 +09:00
parent 5903211eb9
commit 8096837b13

View File

@ -433,6 +433,10 @@ class YouTubeAnalyticsService:
logger.debug("[YouTubeAnalyticsService._fetch_region] SUCCESS") logger.debug("[YouTubeAnalyticsService._fetch_region] SUCCESS")
return result return result
# 5xx/네트워크 오류 재시도 설정 (구글 backendError 등 일시적 장애 대응)
_MAX_RETRIES = 3
_RETRY_BACKOFF_SECONDS = (0.5, 1.0, 2.0)
async def _call_api( async def _call_api(
self, self,
params: dict[str, str], params: dict[str, str],
@ -453,15 +457,18 @@ class YouTubeAnalyticsService:
Raises: Raises:
YouTubeQuotaExceededError: 할당량 초과 (429) YouTubeQuotaExceededError: 할당량 초과 (429)
YouTubeAuthError: 인증 실패 (401, 403) YouTubeAuthError: 인증 실패 (401, 403)
YouTubeAPIError: 기타 API 오류 YouTubeAPIError: 기타 API 오류 (5xx/네트워크 오류는 최대 3 재시도 발생)
Note: Note:
- 타임아웃: 30 - 타임아웃: 30
- 할당량 초과 자동으로 YouTubeQuotaExceededError 발생 - 할당량 초과 자동으로 YouTubeQuotaExceededError 발생
- 인증 실패 자동으로 YouTubeAuthError 발생 - 인증 실패 자동으로 YouTubeAuthError 발생
- 5xx 응답 네트워크 오류는 지수 백오프(0.5s1s2s) 최대 3 재시도
""" """
headers = {"Authorization": f"Bearer {access_token}"} headers = {"Authorization": f"Bearer {access_token}"}
last_error: Exception | None = None
for attempt in range(self._MAX_RETRIES + 1):
try: try:
async with httpx.AsyncClient(timeout=30.0) as client: async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.get( response = await client.get(
@ -488,16 +495,29 @@ class YouTubeAnalyticsService:
return response.json() return response.json()
except (YouTubeAuthError, YouTubeQuotaExceededError): except (YouTubeAuthError, YouTubeQuotaExceededError):
raise # 이미 처리된 예외는 그대로 전파 raise # 이미 처리된 예외는 재시도 없이 그대로 전파
except httpx.HTTPStatusError as e: except httpx.HTTPStatusError as e:
logger.error( logger.error(
f"[YouTubeAnalyticsService._call_api] HTTP_ERROR - " f"[YouTubeAnalyticsService._call_api] HTTP_ERROR - "
f"status={e.response.status_code}, body={e.response.text[:500]}" f"status={e.response.status_code}, body={e.response.text[:500]}"
) )
# 4xx는 재시도해도 동일하게 실패하므로 즉시 전파, 5xx만 재시도
if e.response.status_code < 500:
raise YouTubeAPIError(f"HTTP {e.response.status_code}") raise YouTubeAPIError(f"HTTP {e.response.status_code}")
last_error = YouTubeAPIError(f"HTTP {e.response.status_code}")
except httpx.RequestError as e: except httpx.RequestError as e:
logger.error(f"[YouTubeAnalyticsService._call_api] REQUEST_ERROR - {e}") logger.error(f"[YouTubeAnalyticsService._call_api] REQUEST_ERROR - {e}")
raise YouTubeAPIError(f"네트워크 오류: {e}") last_error = YouTubeAPIError(f"네트워크 오류: {e}")
except Exception as e: except Exception as e:
logger.error(f"[YouTubeAnalyticsService._call_api] UNEXPECTED_ERROR - {e}") logger.error(f"[YouTubeAnalyticsService._call_api] UNEXPECTED_ERROR - {e}")
raise YouTubeAPIError(f"알 수 없는 오류: {e}") raise YouTubeAPIError(f"알 수 없는 오류: {e}")
if attempt < self._MAX_RETRIES:
backoff = self._RETRY_BACKOFF_SECONDS[attempt]
logger.warning(
f"[YouTubeAnalyticsService._call_api] RETRY {attempt + 1}/{self._MAX_RETRIES} "
f"in {backoff}s - {last_error}"
)
await asyncio.sleep(backoff)
raise last_error