AwsS3Manager::uploadDirectoryByCredential | upload failed - {"message":"Connection timed out after 1
RCA: AwsS3Manager::uploadDirectoryByCredential | upload failed - Connection timed out
Overview#
What Happened#
2026-05-22 14:35~16:11 UTC 사이에 eu-central-1 리전에서 실행 중인 pano-postprocessor 인스턴스가 me-central-1 (UAE) S3 버킷으로의 파일 업로드 시 반복적인 connection timeout(120초)을 경험했다. hassan-allam 팀의 캡처 3건(36961, 36963, 36966)에서 총 6건의 S3 오류가 발생했으며, 최종적으로 재시도를 통해 모든 304개 파노라마가 성공적으로 처리되었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | TimeoutError |
| exception.message | Connection timed out after 120000ms |
| top_frame | aws-s3.manager.ts:138 |
| env | production, eu-central-1 → me-central-1 (cross-region) |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| hassan-allam | 6 | 파노 리사이즈 업로드 지연 (최종 성공, 데이터 손실 없음) |
Timeline#
- 14:35:25Z — 첫 번째 timeout 발생 (job 101985, capture 36961)
- 14:41:42Z — ECONNRESET 발생 (job 102001, capture 36963)
- 15:28:54Z — job 102003에서 timeout 발생
- 15:49:55Z — 이 클러스터의 첫 번째 에러 (job 102003, capture 36966)
- 16:03:34Z — 이 클러스터의 두 번째 에러 (동일 세션)
- 16:11:31Z — S3 InternalError 발생 (동일 세션)
- 16:17:04Z — job 102003 정상 완료 (304/304 panos, 0 errored)
Error Log#
AwsS3Manager::uploadDirectoryByCredential | upload failed - {"message":"Connection timed out after 120000ms","code":"TimeoutError","time":"2026-05-22T15:49:55.948Z","region":"me-central-1","hostname":"s3.me-central-1.amazonaws.com","retryable":true,"statusCode":500}
Impact#
- Service:
cupixworks-pano-postprocessor-instance - Team: hassan-allam
- 발생 횟수: 2 (이 클러스터), 6 (동일 시간대 전체)
- 최초 발생: 2026-05-22T15:49:55.949Z
- 최근 발생: 2026-05-22T16:03:34.666Z
Root Cause Summary#
eu-central-1에서 실행되는 pano-postprocessor가 me-central-1 S3 버킷으로 cross-region 업로드를 수행할 때, AWS S3 me-central-1 리전의 일시적 네트워크 불안정(intermittent connectivity issue)으로 connection timeout이 발생했다. uploadDirectoryByCredential 메서드가 S3 클라이언트 생성 시 maxRetries, httpOptions.timeout 등을 설정하지 않아 AWS SDK 기본값(timeout 없음, maxRetries 3)에 의존하고 있으며, 개별 파일 업로드 실패를 catch 후 로깅만 하고 재시도하지 않는 구조적 취약점이 있다. 다만, 상위 레벨의 파노 단위 재시도 로직 덕분에 최종적으로 모든 작업이 완료되었다.
Technical Analysis#
Code Path#
- Entry point:
pano-postprocessor-service.ts:165— resize task orchestrator - Upload trigger:
resize-work.ts:141—uploadResizedImage호출 - Failure point:
aws-s3.manager.ts:131-139— S3 upload 시 timeout 발생
S3 클라이언트 생성 시 retry/timeout 설정이 없음:
async uploadDirectoryByCredential({ credential, directory }: { credential: TESLA.UploadCredentials, directory: string }): Promise<void> {
const s3 = new AWS.S3({
apiVersion: '2006-03-01',
signatureVersion: 'v4',
accessKeyId: credential['aws_access_key_id'],
secretAccessKey: credential['aws_secret_access_key'],
sessionToken: credential['aws_session_token'],
endpoint: credential['endpoint'],
s3ForcePathStyle: true,
// maxRetries, httpOptions.timeout 미설정 — SDK 기본값 사용
});
업로드 실패 시 에러를 로깅만 하고 swallow:
await s3.upload({
Bucket: credential.bucket_name!,
Key: bucketKey,
Body: fs.createReadStream(filePath)
}).promise();
logger.debug('AwsS3Manager::uploadDirectoryByCredential | uploaded file - %s', filePath);
uploadedFileCount++;
} catch (error) {
logger.error('AwsS3Manager::uploadDirectoryByCredential | upload failed - %s', JSON.stringify(error));
}
비교: base 패키지의 S3 매니저는 retry 설정이 있음:
AWS.config.update({
region: __region,
maxRetries: 5,
retryDelayOptions: { base: 200 }
});
상위 orchestrator에서 파노 단위 catch가 있어 전체 파이프라인은 보호됨:
const resizeTasks = cpPanos.map((cpPano) => {
return PARALLEL_TASK_LIMIT(async () => {
try {
await this.resizeWork.resizePano(cpPano);
await this.resizeWork.uploadResizedImage(cpPano);
await this.resizeWork.checkStitched(cpPano);
} catch (error) {
logger.error('PanoPostprocessorService::run | resize pano id:%d | error %s', cpPano.panoId, stringifyError(error));
await this.panoPostprocessorManager.updatePanoState(cpPano.panoId!, TESLA.UpdatePanoRequest.StateEnum.Error);
erroredPanoIds.add(cpPano.panoId!);
}
});
});
Log Evidence#
Datadog 검색 쿼리:
service:cupixworks-pano-postprocessor-instance status:error @environment:production "me-central-1"
동일 세션에서 발생한 전체 에러 로그:
{"message":"Connection timed out after 120000ms","code":"TimeoutError","time":"2026-05-22T15:49:55.948Z","region":"me-central-1","hostname":"s3.me-central-1.amazonaws.com","retryable":true,"statusCode":500}
{"message":"Connection timed out after 120000ms","code":"TimeoutError","time":"2026-05-22T16:03:34.665Z","region":"me-central-1","hostname":"s3.me-central-1.amazonaws.com","retryable":true,"statusCode":500}
{"message":"We encountered an internal error. Please try again.","code":"InternalError","region":null,"time":"2026-05-22T16:11:31.430Z","requestId":"32KK34XM70B8KQPT","statusCode":500,"retryable":true}
작업 완료 확인 로그 (16:17:04Z):
PanoPostprocessorService Metric - dd_metric:pano.postprocess.summary&dd_total:304&dd_completed:304&dd_errored:0
동일 시간대 다른 서비스의 me-central-1 접근 성공 (16:21~16:23Z):
cupixworks-any-potree-agent: AwsS3Manager::constructor | region: me-central-1 (성공)
cupixworks-any-voxel-agent: VoxelManager::uploadXYPlaneVoxels | s3Credentials bucket_region: me-central-1 (성공)
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | AWS S3 me-central-1 리전의 일시적 네트워크 불안정 | 동일 시간대 6건의 timeout/ECONNRESET/InternalError, retryable:true, S3 statusCode 500, cupixworks-worker에서도 me-south-1 timeout 발생 |
다른 서비스(potree-agent, voxel-agent)는 16:21경 me-central-1 정상 접근 성공 | Confirmed |
| H2 | 잘못된 S3 credential/endpoint 설정 | — | 동일 세션에서 대부분의 파일(304개)이 성공적으로 업로드됨, 다른 서비스도 동일 리전 정상 사용 | Rejected |
| H3 | eu-central-1 → me-central-1 cross-region 네트워크 경로 특정 호스트 문제 | 모든 에러가 동일 호스트(d7c26710dbba)에서 발생, 같은 시간 다른 호스트의 서비스는 성공 | potree-agent도 eu-central-1에서 실행되나 정상, 여러 job에 걸쳐 발생하여 호스트 특정 문제라고 보기 어려움 | Inconclusive |
| H4 | S3 클라이언트 retry 미설정으로 인한 복구 실패 | pano-postprocessor의 S3 클라이언트에 maxRetries/timeout 명시 없음, base 패키지와 불일치 | SDK 기본 maxRetries=3이 적용되어 일부 재시도는 동작함, 최종적으로 파노 단위 재시도로 복구됨 | Confirmed (기여 요인) |
Fix Recommendation#
즉시 조치 (Critical)#
aws-s3.manager.ts:110-118— S3 클라이언트 생성 시maxRetries: 5,retryDelayOptions: { base: 300 },httpOptions: { timeout: 60000, connectTimeout: 10000 }추가- base 패키지의 retry 설정과 동일하게 맞추어 일관성 확보
단기 개선 (1주 이내)#
uploadDirectoryByCredential메서드에서 개별 파일 업로드 실패 시 에러를 swallow하지 않고, 실패 카운트를 추적하여 threshold 초과 시 throw하도록 변경- 이 에러는
error레벨이 적절하나,retryable:true인 경우warn레벨로 낮추고, 최종 실패 시에만error로 로깅하는 것을 검토
장기 개선 (재발 방지)#
- Cross-region S3 업로드의 안정성을 위해 S3 Transfer Acceleration 또는 리전별 S3 endpoint 선택 로직 도입 검토
- pano-postprocessor의 S3 매니저를 base 패키지의 것으로 통합하거나, 공통 설정을 상속하도록 리팩터링
Monitoring#
- Cross-region S3 업로드 timeout 빈도 모니터링:
service:cupixworks-pano-postprocessor-instance status:error "Connection timed out" "me-central-1"
- S3 업로드 지연시간 메트릭 추가 (per-region p95 latency)
- me-central-1 리전 관련 에러 비율이 급증할 경우 알림 설정
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial