ES /docs

ImageCPObject::downloadFile | response path: /tmp/workspace/zkv5d75d9mwy/zkv5d75d9mwy.jpg, code: 504

RCA: ImageCPObject::downloadFile 504 Gateway Time-out

Overview#

What Happened#

2026-07-11 02:12 KST 무렵 cupixworks-any-thumbnail-agent (us-west-2, production) 에서 원본 이미지 파일을 tesla API 로부터 내려받는 도중 nginx 가 504 Gateway Time-out 을 반환하여 두 건의 asset 썸네일 생성 작업이 실패했다. 실패한 요청은 http://api-tesla.cupix.internal/api/v1/assets/{key}/download 이며, 두 workspace (zkv5d75d9mwy, 3cmc03w8qhqf) 모두 원본 다운로드 단계에서 실패한 뒤 workspace 가 정리되었다.

Quick Facts#

Field Value
exception.class ImageCPObject::downloadFile
exception.message response path: /tmp/workspace/{workspace}/{workspace}.jpg, code: 504, message: Gateway Time-out
top_frame packages/cupix-tesla-thumbnail-agent/src/model/common/image-cpobject.ts:76
runtime Node.js SQS worker (thumbnail agent)
env production, us-west-2

Affected Teams#

Team / Domain Error Count Impact
maryl / thumbnail agent 2 Asset 2건의 썸네일 생성이 원본 다운로드 단계에서 실패. SQS 메시지는 undefined response 로 판정되어 즉시 삭제되므로 재시도 없이 손실.

Timeline#

  1. 2026-07-11 02:11:00 KSTImageCPObject::downloadOriginal | url: http://api-tesla.cupix.internal/api/v1/assets/zkv5d75d9mwy/download (info) — 다운로드 시작
  2. 2026-07-11 02:12:00 KSTImageCPObject::downloadFile | response ... code: 504, message: Gateway Time-out (error) — 첫 번째 asset 실패
  3. 2026-07-11 02:12:00 KSTBaseService::handlingMessageErrors | Error and message object - {"error":"undefined response", ...} (error) — 메시지 삭제 처리
  4. 2026-07-11 02:11:11 KSTImageCPObject::downloadOriginal | url: .../3cmc03w8qhqf/download (info) — 두 번째 다운로드 시작
  5. 2026-07-11 02:12:11 KST — 동일 오류로 두 번째 asset 실패

Error Log#

Datadog Logs

text
ImageCPObject::downloadFile | response path: /tmp/workspace/zkv5d75d9mwy/zkv5d75d9mwy.jpg, code: 504, message: Gateway Time-out

Impact#

  • Service: cupixworks-any-thumbnail-agent
  • Team: maryl
  • 발생 횟수: 2
  • 최초 발생: 2026-07-11 02:12 KST
  • 최근 발생: 2026-07-11 02:12 KST

Root Cause Summary#

ImageCPObject::downloadFile 이 tesla 내부 API (http://api-tesla.cupix.internal/api/v1/assets/{key}/download) 로 원본 이미지를 요청했을 때 앞단 nginx 가 upstream 응답을 기다리다 timeout 되어 HTTP 504 Gateway Time-out 을 반환했다. 이는 thumbnail agent 자체의 논리 버그가 아니라 업스트림 tesla API 의 asset download endpoint 응답 지연 이 근본 원인이다. 다만 agent 쪽에는 getApiErrorToDeleteMessage 가 5xx 응답(response 없이 res 객체가 그대로 reject 됨)을 "undefined response" 로 판정해 재시도 없이 SQS 메시지를 삭제 하는 동작이 있어, 일시적 gateway timeout 이 곧바로 asset 별 영구 실패로 이어졌다.

Technical Analysis#

Code Path#

  • Entry point: packages/cupix-tesla-thumbnail-agent/src/thumbnail-service.ts:58 (ThumbnailService.run) — SQS 메시지 수신 후 asset 모델 초기화
  • Step: packages/cupix-tesla-thumbnail-agent/src/thumbnail-service.ts:77 (supportModel.downloadOriginal())
  • Step: packages/cupix-tesla-thumbnail-agent/src/model/common/image-cpobject.ts:164-181 (ImageCPObject.downloadOriginaldownloadFile)
  • Failure point: packages/cupix-tesla-thumbnail-agent/src/model/common/image-cpobject.ts:74-77 (request.get(...).on('response') 분기, statusCode !== 200 이면 error 로그 후 reject)
  • Downstream error handling: packages/base/src/base-service.ts:240-275 (getApiErrorToDeleteMessage), packages/base/src/base-service.ts:290-313 (handlingMessageErrors)
packages/cupix-tesla-thumbnail-agent/src/model/common/image-cpobject.ts:73-85typescript
			sendReq
				.on('response', res => {
					if (res.statusCode !== 200) {
						logger.error('ImageCPObject::downloadFile | response path: %s, code: %d, message: %s', filePath, res.statusCode, res.statusMessage);
						reject(cupixAuth.handleError(res));
					} else {
						sendReq.pipe(fileStream);
					}
				})
				.on('error', err => {
					logger.error('ImageCPObject::downloadFile | path: %s, error: %s', filePath, JSON.stringify(err));
					reject(cupixAuth.handleError(err));
				});

에이전트는 tesla API 로 원본 이미지를 GET 하고, upstream 이 200 이 아니면 즉시 reject 한다. 여기서 res 는 raw http.IncomingMessage 이며 res.response 는 존재하지 않는다.

packages/base/src/base-service.ts:240-275typescript
	private getApiErrorToDeleteMessage = (error: any): any => {
		if (error == undefined) { ... return 'undefined error'; }
		if (error.errno != undefined && error.code != undefined && error.syscall != undefined) {
			logger.warn('BaseService::getApiErrorToDeleteMessage | nodejs common system error', error);
			return;
		}
		const response = CPUtils.isJsonString(error) ? JSON.parse(error) : error.response;
		if (response == undefined) {
			logger.warn('BaseService::getApiErrorToDeleteMessage | undefined response', error);
			return 'undefined response';
		}

		const statusCode = response.statusCode ? Number(response.statusCode) : undefined;
		// ...
		if (statusCode != undefined && statusCode >= 400 && statusCode <= 500) {
			if (statusCode === 401) return;
			return errorMsg;
		}
		return;
	};

기대 동작: 5xx (gateway timeout 포함) 은 일시적일 수 있으므로 SQS 메시지를 그대로 두어 SQS 의 재전송 및 MaxReceiveCount 정책에 맡긴다.

실제 동작: handleError 가 reject 인자로 넘긴 raw response 는 error.response 필드가 없어 undefined response 분기로 떨어진다. getApiErrorToDeleteMessage 는 truthy 문자열 "undefined response" 를 반환하고, handlingMessageErrors (base-service.ts:301) 은 이 값을 "삭제 대상" 으로 간주해 deleteByMessage 를 즉시 호출한다. 결과적으로 504 로 실패한 메시지는 재시도 없이 큐에서 제거된다.

Log Evidence#

Datadog query used:

text
service:cupixworks-any-thumbnail-agent (zkv5d75d9mwy OR 3cmc03w8qhqf)

첫 번째 asset (zkv5d75d9mwy) 의 이벤트 시퀀스:

text
2026-07-11 02:11:00  info   ImageCPObject::downloadOriginal | url: http://api-tesla.cupix.internal/api/v1/assets/zkv5d75d9mwy/download, path: /tmp/workspace/zkv5d75d9mwy/zkv5d75d9mwy.jpg
2026-07-11 02:12:00  error  ImageCPObject::downloadFile | response path: /tmp/workspace/zkv5d75d9mwy/zkv5d75d9mwy.jpg, code: 504, message: Gateway Time-out
2026-07-11 02:12:00  error  BaseService::handlingMessageErrors | Error and message object - {"error":"undefined response","sqsMessage":{"MessageId":"bf9bd366-fb98-42b8-9959-9335f2b1196b","Attributes":{"ApproximateReceiveCount":"1"}}}
2026-07-11 02:12:00  info   BaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/zkv5d75d9mwy

두 번째 asset (3cmc03w8qhqf) 는 11 초 후 완전히 동일한 시퀀스로 실패했다 (MessageId: 3e79e753-2234-4ea0-96b8-2f416e97f210, ApproximateReceiveCount: 1). 두 경우 모두 첫 시도(retry 0)에서 삭제됐다.

같은 시간대에 다른 형태의 nginx 504 도 다수 관찰돼 upstream 지연이 지속되고 있었음을 뒷받침한다:

text
2026-07-11 02:19:44  error  ImageCPObject::putThumbnail | Not json format - <html>
<head><title>504 Gateway Time-out</title></head>
<body>
<center><h1>504 Gateway Time-out</h1></center>
<hr><center>nginx</center>
</body>
</html>

putThumbnail 응답 본문에 nginx 의 표준 504 HTML 이 그대로 담겨 있어 timeout 이 tesla 앞단 nginx 에서 발생했음을 확정할 수 있다. Datadog query:

text
service:cupixworks-any-thumbnail-agent status:error

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 Tesla API (/api/v1/assets/{key}/download) 앞단 nginx 가 upstream timeout 으로 504 를 반환했다 `ImageCPObject::putThumbnail Not json format - ...504 Gateway Time-out...
nginx
로그 (2026-07-11 02:19:44 KST 등 다수), 실패 URL 이http://api-tesla.cupix.internal/api/v1/assets/{key}/download` 로 tesla 앞단, first_seen~last_seen 이 1분 이내 클러스터링
H2 Thumbnail agent 의 request timeout 설정이 짧아 자체적으로 abort 한 것 에러 메시지 텍스트 "Gateway Time-out" 와 statusCode 504 는 서버가 응답한 값이며, 클라이언트 abort 이면 .on('error') 분기로 들어가 다른 로그 형태가 되었어야 한다 (image-cpobject.ts:82-85) Rejected
H3 Asset 이 없거나 권한이 없어 tesla API 가 4xx 을 반환했다 응답 코드가 504 이며, 다른 asset 관련 오류(ENT4000 Bim not found) 와는 별개 시각에 발생 (02:27:30 KST). 클러스터 대상 asset key 는 존재 가정으로 다운로드 시작 로그(info) 가 남아있음 Rejected
H4 5xx 인 일시적 오류인데도 agent 가 SQS 메시지를 즉시 삭제해 재시도 기회를 잃는 부가 버그가 존재 handlingMessageErrors 로그의 "error":"undefined response" (base-service.ts:250-252), ApproximateReceiveCount: 1 이 두 asset 모두 남아있음, getApiErrorToDeleteMessage"undefined response" 문자열을 반환 → truthy → deleteByMessage 호출 경로 (base-service.ts:301-304) Confirmed (aggravating factor)

Fix Recommendation#

즉시 조치 (Critical)#

  • 없음 (외부 원인): 근본 원인은 tesla API 앞단 nginx 의 upstream timeout. 인프라/tesla 팀에 api-tesla.cupix.internal/api/v1/assets/{key}/download 응답 지연 원인 확인 요청. 발생 시점 (2026-07-11 02:12 KST, us-west-2) 의 tesla API, DB, S3 latency 를 함께 조사해야 한다.

단기 개선 (1주 이내)#

  • packages/base/src/base-service.ts:240-275 getApiErrorToDeleteMessage 의 5xx 처리 명시화: 현재 undefined response 분기에서 문자열을 반환해 삭제 대상으로 오인식되는 흐름이 문제. 아래 두 방향 중 하나로 보정 필요.
    • image-cpobject.ts:77 에서 cupixAuth.handleError(res) 대신 { response: res } 형태의 정형화된 error 객체로 reject 하도록 조정하여 downstream 이 statusCode 를 볼 수 있게 한다.
    • 또는 getApiErrorToDeleteMessage 에서 error.statusCode 를 직접 확인하는 폴백 분기를 추가해 5xx 는 return; (삭제 대상 아님) 처리한다.
  • image-cpobject.ts:53-91 downloadFile 에 요청 타임아웃/재시도: request.gettimeout 옵션을 명시하고, 5xx 응답은 지수 백오프로 N 회 재시도 후 최종 실패시 reject. packages/api/src/authentication/cupix-auth.ts:59retryable 헬퍼가 존재하므로 이를 활용하는 방향으로 조정.

장기 개선 (재발 방지)#

  • tesla API asset download endpoint 의 SLI/SLO 정의 및 알림: p95 latency, 5xx 비율에 대한 SLO 를 정의하고 상시 모니터링. 지금은 downstream 에서 개별 오류로 흩어져 upstream degradation 이 지연 감지된다.
  • SQS 메시지 삭제 정책 리팩터: undefined response 문자열 반환으로 삭제 여부를 결정하는 관용구는 재발 위험이 크다. 삭제 대상은 statusCode 를 명시적으로 검사(4xx 만) 하도록 API 를 재설계.
  • Nginx upstream timeout 완화 또는 다운로드 endpoint 를 S3 presigned URL 직접 리다이렉트로 전환: 이미지 다운로드가 nginx 를 통과할 필요가 없도록 아키텍처 검토.

Monitoring#

Datadog dashboard/timeseries widget 용 쿼리 (release dashboard 로 그대로 붙일 수 있는 형태):

  • Thumbnail agent 의 downloadFile 실패 (모든 statusCode):
text
service:cupixworks-any-thumbnail-agent status:error "ImageCPObject::downloadFile"
  • Thumbnail agent 관련 504 전체 (downloadFile + putThumbnail nginx HTML 응답 포함):
text
service:cupixworks-any-thumbnail-agent status:error "504 Gateway Time-out"
  • 같은 근본 원인의 tesla asset download 5xx 응답 (tesla API 측):
text
service:cupixworks-api status:error @http.url_details.path:"/api/v1/assets/*/download" @http.status_code:[500 TO 599]
  • SQS 메시지가 5xx 로 즉시 삭제되는 케이스 추적 (aggravating factor):
text
service:cupixworks-any-thumbnail-agent status:error "handlingMessageErrors" "undefined response"

Risk Assessment#

  • Risk level: medium — 개별 asset 썸네일 유실은 사용자 체감 (missing thumbnail) 로 이어지지만, 시스템 전체 장애는 아니며 발생 빈도가 낮다(관측 기간 내 2건). 다만 aggravating factor 인 "5xx 즉시 삭제" 는 향후 tesla 지연 시 대량 유실 위험이 있어 우선 완화 대상.
  • 예상 복잡도: standard — 근본 원인 조사는 tesla/infra 협조 필요 (외부 조율). 코드 측 개선(재시도 + 삭제 정책 보정) 은 pure-code 로 처리 가능하나 SQS 재시도 동작 변경이 다른 agent 에도 영향을 주므로 회귀 테스트 필요.