BaseService::handlingMessageErrors | Error and message object - {"error":{"errno":-110,"code":"ETIME
RCA: BaseService::handlingMessageErrors | ETIMEDOUT during thumbnail upload
Error Log#
BaseService::handlingMessageErrors | Error and message object - {"error":{"errno":-110,"code":"ETIMEDOUT","syscall":"read"},"sqsMessage":{"MessageId":"61d03ebd-9499-4634-ab52-276fba55406b","Attributes":{"ApproximateReceiveCount":"1"}}}
Impact#
- Service:
cupixworks-any-thumbnail-agent - Team: hassan-allam
- 발생 횟수: 1
- 최초 발생: 2026-04-09T10:55:13.144Z
- 최근 발생: 2026-04-09T10:55:13.144Z
Root Cause Summary#
cupix-tesla-thumbnail-agent가 eu-central-1 리전의 호스트 ip-10-1-40-3에서 asset bv6n1yt7bmvp에 대한 썸네일 이미지를 업로드하는 도중 TLS 연결의 read syscall에서 타임아웃이 발생했습니다. ImageCPObject::uploadFile 메서드는 request.put()을 호출할 때 timeout 옵션을 설정하지 않으며 retry 로직도 없어, 네트워크 일시적 장애 시 단일 시도 실패로 에러가 전파됩니다. SQS 메시지의 ApproximateReceiveCount가 1이므로 첫 번째 처리 시도에서 발생한 일시적 네트워크 장애이며, SQS의 visibility timeout 이후 자동 재시도됩니다.
Technical Analysis#
Code Path#
- Entry point:
BaseService::checkingQueue()(base/src/base-service.ts:110) — SQS 큐에서 메시지를 수신하고 처리 루프를 시작합니다.
// applications/agents/packages/base/src/base-service.ts:145-151
private runByMessages = async (): Promise<void> => {
logger.debug('BaseService::runByMessages | begin');
for await (const message of this.messageList) {
await this.runByMessage(message);
}
logger.debug('BaseService::runByMessages | end');
};
- Message processing:
BaseService::runByMessage()(base/src/base-service.ts:153-191) — 메시지 body를 파싱하고 인증 후ThumbnailService.run()을 호출합니다.
// applications/agents/packages/base/src/base-service.ts:168-180
await TraceUtils.activateSpan(span, async () => {
this.setLogMeta(msgObject);
logger.info('BaseService::runByMessage | id: %d', targetId);
await this.authenticateByMessage(msgObject);
await this.run(targetId, msgObject); // ThumbnailService.run() 호출
await this.cleanUpAnythingRelatedModel();
await this.deleteByMessage(message);
});
- Failure point:
ImageCPObject::uploadFile()(cupix-tesla-thumbnail-agent/src/model/common/image-cpobject.ts:93-117) — 파일 업로드 시request.put()호출에 timeout 설정이 없어 TLS read에서 ETIMEDOUT 발생.
// cupix-tesla-thumbnail-agent/src/model/common/image-cpobject.ts:96-116
const sendReq = request.put(url, {
headers: headers,
body: fs.readFileSync(filePath) // timeout 옵션 없음
});
sendReq
.on('complete', async () => {
await CPUtils.sleep(100);
resolve();
})
.on('response', res => {
if (res.statusCode !== 200) {
reject(cupixAuth.handleError(res));
}
})
.on('error', err => {
logger.error('ImageCPObject::uploadFile | path: %s, error: %s', path, JSON.stringify(err));
reject(cupixAuth.handleError(err)); // ETIMEDOUT 에러가 여기서 reject
});
- Error classification:
BaseService::getApiErrorToDeleteMessage()(base/src/base-service.ts:245-247) —errno,code,syscall속성이 있는 시스템 에러로 인식하여undefined를 반환합니다. 이로 인해 SQS 메시지가 삭제되지 않고 큐에 남아 재처리됩니다.
// applications/agents/packages/base/src/base-service.ts:245-247
if (error.errno != undefined && error.code != undefined && error.syscall != undefined) {
logger.warn('BaseService::getApiErrorToDeleteMessage | nodejs common system error', error);
return; // undefined 반환 → 메시지 삭제하지 않음
}
- 기대 동작: 업로드 실패 시 retry 로직이 동작하여 일시적 네트워크 장애를 자동 복구해야 함.
- 실제 동작: retry 없이 단일 시도 실패로 에러 전파. SQS의 visibility timeout 기반 재시도에만 의존.
Log Evidence#
에러 발생 시점에 4개의 로그가 기록되었습니다 (2ms 이내):
Datadog 검색 쿼리:
service:cupixworks-any-thumbnail-agent status:(error OR warn) @environment:production
1. warn 로그 (10:55:13.142Z) — CupixAuth::handleError:
CupixAuth::handleError | Undefined response: {"stack":"Error: read ETIMEDOUT\n at TLSWrap.onStreamRead (node:internal/stream_base_commons:218:20)\n at TLSWrap.callbackTrampoline (node:internal/async_hooks:130:17)","message":"read ETIMEDOUT","errno":-110,"code":"ETIMEDOUT","syscall":"read"}
2. error 로그 (10:55:13.142Z) — ImageCPObject::uploadFile:
ImageCPObject::uploadFile | path: { default: [Object], resolve: [Getter], normalize: [Getter], isAbsolute: [Getter], join: [Getter], relative: [Getter], toNamespacedPath: [Getter], dirname: [Getter], basename: [Getter], extname: [Getter], format: [Getter], parse: [Getter], matchesGlob: [Getter], sep: [Getter], delimiter: [Getter], win32: [Getter], posix: [Getter], _makeLong: [Getter] }, error: {"errno":-110,"code":"ETIMEDOUT","syscall":"read"}
참고:
path파라미터에 파일 경로 대신 Node.jspath모듈 객체가 출력되고 있습니다. 이는uploadFile의 error 핸들러에서 로깅할 때path변수가 import된path모듈을 참조하는 스코핑 문제로 보입니다.
3. warn 로그 (10:55:13.144Z) — raw error:
read ETIMEDOUT
- Stack trace:
Error: read ETIMEDOUT at TLSWrap.onStreamRead (node:internal/stream_base_commons:218:20)
4. error 로그 (10:55:13.144Z) — BaseService::handlingMessageErrors:
BaseService::handlingMessageErrors | Error and message object - {"error":{"errno":-110,"code":"ETIMEDOUT","syscall":"read"},"sqsMessage":{"MessageId":"61d03ebd-9499-4634-ab52-276fba55406b","Attributes":{"ApproximateReceiveCount":"1"}}}
컨텍스트 정보 (로그 메타데이터):
- Host:
ip-10-1-40-3.eu-central-1.compute.internal - Region: eu-central-1
- User: paul.esguerra@stantec.com (id: 3775)
- Team: hassan-allam (id: 105)
- Asset key:
bv6n1yt7bmvp - Session:
5ebb9eb99cc1c2d0ef9c627c661eebbac5f3566c
7일간 에러 빈도 분석:
service:cupixworks-any-thumbnail-agent status:error @environment:production
7일간 총 24개 에러 중 ETIMEDOUT은 이 1건만 발견됨. 나머지 에러는 HTTP 500 (Internal Server Error), 404 (Not Found), 403 (Bim/Floorplan not found) 등 API 레벨 에러. 동일 시간대 다른 호스트에서는 정상적으로 SQS 메시지를 처리 중이었으며 (200+ info 로그 확인), 이 ETIMEDOUT은 ip-10-1-40-3 호스트에서만 발생한 일시적 네트워크 장애입니다.
Fix Recommendation#
즉시 조치 (Critical)#
별도 즉시 조치 불필요. SQS 메시지가 삭제되지 않고 큐에 남아있으므로 (ApproximateReceiveCount: 1), SQS visibility timeout 이후 자동으로 재처리됩니다. 단발성 네트워크 장애이며 서비스 전체에는 영향이 없습니다.
단기 개선 (1주 이내)#
-
ImageCPObject::uploadFile에 timeout 옵션 추가 (cupix-tesla-thumbnail-agent/src/model/common/image-cpobject.ts:96-98)request.put()에timeout옵션 (예: 30초)을 설정하여 무한 대기를 방지해야 합니다.- 현재는 timeout 설정이 없어 TLS 연결이 끊어질 때까지 무기한 대기합니다.
-
업로드 retry 로직 추가 (
image-cpobject.ts:93-117)- 다른 agent에서 사용하는
TransferManager의 retry 패턴 (cupix-capture-3d-reconstruction-agent/src/manager/transfer.manager.ts:175-193)을 참고하여, ETIMEDOUT 같은 일시적 에러에 대한 retry (예: 3회, exponential backoff)를 구현해야 합니다. - 현재는 SQS의 재처리에만 의존하고 있어 불필요한 지연이 발생합니다.
- 다른 agent에서 사용하는
-
path변수 스코핑 문제 수정 (image-cpobject.ts:115)- error 핸들러의
logger.error('... path: %s', path, ...)에서path가 파일 경로가 아닌 Node.jspath모듈을 참조하고 있습니다.filePath파라미터를 사용하도록 수정해야 합니다.
- error 핸들러의
장기 개선 (재발 방지)#
-
공통 HTTP 클라이언트 모듈 도입 — 모든 agent가 동일한 timeout, retry, connection pooling 설정을 사용하도록
base패키지에 공통 HTTP 클라이언트를 구현. 현재ImageCPObject,TransferManager등이 각각 독립적으로request라이브러리를 사용하고 있어 설정이 일관되지 않음. -
request라이브러리 교체 검토 —request라이브러리는 deprecated 상태.axios또는undici등 유지보수되는 HTTP 클라이언트로 마이그레이션을 검토. 내장 retry, timeout, keep-alive 지원이 더 우수합니다.
Monitoring#
- ETIMEDOUT 에러 발생 빈도 모니터링:
service:cupixworks-any-thumbnail-agent "ETIMEDOUT" status:error @environment:production
- 업로드 실패율 전체 추적:
service:cupixworks-any-thumbnail-agent "ImageCPObject::uploadFile" status:error @environment:production
- SQS 메시지 재처리 횟수 (높은 ApproximateReceiveCount) 알림 추가 검토
Risk Assessment#
- Risk level: low
- 예상 복잡도: standard
- 단발성 네트워크 장애이며 SQS 재처리로 자동 복구됩니다. 7일간 동일 에러 재발 없음. 단, timeout/retry 부재는 잠재적 위험이므로 단기 개선을 권장합니다.