ImageCPObject.downloadFile() missing timeout — hangs on TLS stall
RCA: ImageCPObject::downloadFile ETIMEDOUT
Overview#
What Happened#
2026-05-22 06:26 UTC에 cupixworks-any-thumbnail-agent 서비스에서 Asset 썸네일 생성을 위한 파일 다운로드 중 TCP read timeout(ETIMEDOUT)이 발생했다. 다운로드가 06:10:38에 시작되어 약 15분 30초 후에 타임아웃으로 실패했으며, SQS 재시도 시에도 업로드 단계에서 동일한 ETIMEDOUT이 재발했다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | ETIMEDOUT |
| exception.message | ImageCPObject::downloadFile | path: /tmp/workspace/8mqd7168rx4l/8mqd7168rx4l.jpg, error: {"errno":-110,"code":"ETIMEDOUT","syscall":"read"} |
| top_frame | image-cpobject.ts:downloadFile |
| runtime | Node.js (ECS Fargate) |
| env | production, eu-central-1 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| hassan-allam | 1 | Asset 썸네일 생성 지연 (SQS 재시도로 최종 실패) |
Timeline#
- 06:10:38Z — SQS 메시지 수신, Asset
8mqd7168rx4l썸네일 작업 시작 - 06:10:38Z —
api-tesla.cupix.internal로 다운로드 요청 시작 - 06:26:09Z — TCP read timeout 발생 (ETIMEDOUT, ~15.5분 경과)
- 08:14:44Z — SQS 재시도 (2차): 다운로드 성공, 썸네일/커버 생성 성공
- 08:32:19Z — 업로드 단계에서 동일한 ETIMEDOUT 발생, 작업 최종 실패
Error Log#
text
ImageCPObject::downloadFile | path: /tmp/workspace/8mqd7168rx4l/8mqd7168rx4l.jpg, error: {"errno":-110,"code":"ETIMEDOUT","syscall":"read"}
Impact#
- Service:
cupixworks-any-thumbnail-agent - Team: hassan-allam
- 발생 횟수: 1
- 최초 발생: 2026-05-22T06:26:09.786Z
- 최근 발생: 2026-05-22T06:26:09.786Z
Root Cause Summary#
ImageCPObject.downloadFile() 메서드에서 request.get() 호출 시 timeout 옵션이 설정되어 있지 않아 OS 레벨 TCP timeout에 전적으로 의존하고 있다. 내부 API 서버(api-tesla.cupix.internal)와의 연결에서 데이터 전송 중 TLS 스트림이 멈췄을 때, 합리적인 시간 내에 실패 처리하지 못하고 ~15분간 대기 후 커널 TCP timeout으로 실패했다. 재시도 로직도 없어 일시적 네트워크 불안정에 취약한 구조이다.
Technical Analysis#
Code Path#
- Entry point:
BaseService.runByMessage()— SQS 메시지 수신 및 작업 시작 ThumbnailService.newModel()— Asset 모델 생성 (id: NaN로깅 버그 — 문자열 ID를 parseInt 처리)ImageCPObject.downloadOriginal()— 다운로드 URL 구성 및downloadFile호출- Failure point:
image-cpobject.ts:downloadFile—request.get()호출에서 timeout 없이 대기
applications/agents/packages/cupix-tesla-thumbnail-agent/src/model/common/image-cpobject.ts:53-91typescript
private downloadFile = (url: string, filePath: string): Promise<void> => new Promise((resolve, reject) => {
const cupixAuth = this.cupixAuth;
cupixAuth.checkToken()
.then(() => {
const fileStream = fs.createWriteStream(filePath);
const sendReq = request.get(url, {
headers: {
'X-CUPIX-AUTH': cupixAuth.accessToken
}
// timeout 옵션 없음 — OS TCP timeout에 의존
});
// ...
sendReq
.on('error', err => {
logger.error('ImageCPObject::downloadFile | path: %s, error: %s', filePath, JSON.stringify(err));
reject(cupixAuth.handleError(err));
});
});
});
기대 동작: HTTP GET 요청에 적절한 timeout이 설정되어, 응답이 지연될 경우 빠르게 실패 후 재시도. 실제 동작: timeout 미설정으로 인해 커널 TCP read timeout(~15.5분)까지 무한 대기 후 ETIMEDOUT 발생.
Log Evidence#
사용한 Datadog 쿼리:
text
service:cupixworks-any-thumbnail-agent status:error @environment:production "ImageCPObject::downloadFile"
text
service:cupixworks-any-thumbnail-agent @environment:production "8mqd7168rx4l"
핵심 로그 타임라인:
text
06:10:38.521Z - BaseService::runByMessage | id: NaN
06:10:38.579Z - CupixAuth::setSession | session_id: 2fb0e3db...
06:10:38.581Z - ThumbnailService::newModel | model id: NaN, type: Asset
06:10:38.622Z - ImageCPObject::downloadOriginal | url: http://api-tesla.cupix.internal/api/v1/assets/8mqd7168rx4l/download
06:26:09.786Z - ImageCPObject::downloadFile | path: /tmp/workspace/8mqd7168rx4l/8mqd7168rx4l.jpg, error: {"errno":-110,"code":"ETIMEDOUT","syscall":"read"}
06:26:09.787Z - CupixAuth::handleError | Undefined response (TLSWrap.onStreamRead stack trace)
06:26:09.791Z - BaseService::handlingMessageErrors (ApproximateReceiveCount: 1)
2차 시도 (SQS 재시도):
text
08:14:44Z - 다운로드 성공 (~1초 소요)
08:14:45-50Z - 썸네일 생성 성공 (38,944 bytes, 5,358ms)
08:14:52-54Z - 커버 생성 성공 (1,691,704 bytes, 2,396ms)
08:32:19.752Z - ImageCPObject::uploadFile ETIMEDOUT (업로드 단계에서 동일 에러 재발)
같은 시간대 서비스 전반의 네트워크 불안정 증거:
text
04:29-04:58Z - 다수의 504 Gateway Timeout (ImageCPObject::putThumbnail, nginx)
06:03-06:04Z - ImageCPObject::uploadFile HTTP 500 Internal Server Error
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | timeout 미설정으로 인한 TCP read 무한 대기 | request.get() 호출에 timeout 옵션 없음 (image-cpobject.ts:60). 15.5분간 대기 후 OS TCP timeout 발생. request 라이브러리 문서상 timeout 미설정 시 OS 의존 확인. |
— | Confirmed |
| H2 | Asset 파일 크기가 너무 커서 다운로드 실패 | 첫 시도에서 15분 대기 | 2차 시도에서 동일 파일을 ~1초에 다운로드 성공. 파일 크기 문제가 아닌 네트워크 연결 문제 | Rejected |
| H3 | 내부 API 서버(api-tesla.cupix.internal) 가용성 문제 |
같은 시간대 504, 500 에러 다수 발생. 다른 작업들은 정상 완료됨 | 대부분의 동시 작업이 정상 처리됨 — 전면 장애가 아닌 간헐적 연결 문제 | Inconclusive |
Fix Recommendation#
즉시 조치 (Critical)#
image-cpobject.ts의downloadFile메서드에서request.get()호출에timeout옵션 추가 (예:timeout: 60000— connection + read 각각 60초)- 동일 파일의
uploadFile메서드에도 동일하게 timeout 설정
단기 개선 (1주 이내)#
downloadFile과uploadFile에 재시도 로직 추가 (exponential backoff, 최대 3회)CupixAuth.retryable()헬퍼가 이미 존재하므로 이를 활용id: NaN로깅 버그 수정 — Asset의 문자열 ID를 parseInt 없이 그대로 로깅
장기 개선 (재발 방지)#
- deprecated된
request라이브러리(v2.88.2)를axios또는undici로 마이그레이션 (코드베이스의CupixAuth는 이미 axios 사용 중) - 다운로드/업로드 작업에 대한 circuit breaker 패턴 도입
- ECS Fargate에서 내부 API로의 네트워크 경로 모니터링 추가
Monitoring#
- 다운로드/업로드 소요 시간 메트릭 추가 및 P99 > 30초 시 알림
- Datadog 쿼리 예시:
text
service:cupixworks-any-thumbnail-agent status:error "ETIMEDOUT"
text
service:cupixworks-any-thumbnail-agent "downloadFile" @duration:>30000
Risk Assessment#
- Risk level: low
- 예상 복잡도: standard — timeout 추가는 단순하지만
request라이브러리 마이그레이션은 테스트 필요