PanoPostprocessorService::run | mask pano id:80244708 | error {"stack":"AxiosError: Request failed w
RCA: PanoPostprocessorService::run | mask pano | AxiosError 503 on S3 signed URL upload
Error Log#
PanoPostprocessorService::run | mask pano id:80244708 | error {"stack":"AxiosError: Request failed with status code 503
at settle (/tmp/agent/dist/node_modules/axios/dist/node/axios.cjs:2304:7)
at IncomingMessage.handleStreamEnd (/tmp/agent/dist/node_modules/axios/dist/node/axios.cjs:3772:13)
at IncomingMessage.emit (node:events:536:35)
at IncomingMessage.emit (node:domain:489:12)
at endReadableNT (node:internal/streams/readable:1698:12)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
at Axios.request (/tmp/agent/dist/node_modules/axios/dist/node/axios.cjs:5110:41)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async AwsS3Manager.uploadBySignedUrl (/tmp/agent/dist/app.cjs:6446:22)
at async MaskWork.changeMask (/tmp/agent/dist/app.cjs:6709:5)
at async /tmp/agent/dist/app.cjs:6851:17","message":"Request failed with status code 503","name":"AxiosError","code":"ERR_BAD_RESPONSE","config":{},"status":503}
Impact#
- Service:
cupixworks-pano-postprocessor-instance - Team: evansgc
- 발생 횟수: 24 (filebeat 중복 포함, 실제 고유 이벤트 4건 -- pano ID 3건 + 별도 세션 1건)
- 최초 발생: 2026-04-06T22:10:30.942Z
- 최근 발생: 2026-04-07T03:00:19.886Z
Root Cause Summary#
AwsS3Manager.uploadBySignedUrl 메서드가 S3 pre-signed URL로 마스크 이미지를 업로드할 때, S3가 HTTP 503 (Service Unavailable)을 반환했습니다. 이 메서드는 retry 로직이 전혀 없어 일시적인 S3 throttling/overload 응답에도 즉시 실패합니다. 동일 시간대에 cupixworks-worker의 VoxelService, RecordGeoCoordinate 등 다른 서비스에서도 S3/외부 서비스 503 에러가 다수 발생하여, AWS S3 인프라 차원의 일시적 장애가 근본 원인으로 판단됩니다. 코드 측에서는 이러한 transient failure에 대한 retry/backoff 처리가 부재하여 장애가 사용자에게 그대로 전파되었습니다.
Technical Analysis#
Code Path#
- Entry point:
pano-postprocessor-service.ts:46--init()에서run()호출 - Mask 처리 루프:
pano-postprocessor-service.ts:129-138--cpPanos.map()으로 병렬(최대 10개) mask 작업 실행
// pano-postprocessor-service.ts:129-138
const maskTasks = cpPanos.map((cpPano) => {
return PARALLEL_TASK_LIMIT(async () => {
try {
await this.maskWork.changeMask(cpPano, maskTypeOption);
} catch (error) {
logger.error('PanoPostprocessorService::run | mask pano id:%d | error %s', cpPano.panoId, stringifyError(error));
await this.panoPostprocessorManager.updatePanoState(cpPano.panoId!, TESLA.UpdatePanoRequest.StateEnum.Error);
}
});
});
MaskWork.changeMask:mask-work.ts:90-100--createMask로 pre-signed URL을 받아uploadBySignedUrl호출
// mask-work.ts:90-100
async changeMask(cpPano: CPPano, maskType: 'face' | 'facebody') {
if (!cpPano.panoId || !cpPano.maskImagePath || !fs.existsSync(cpPano.maskImagePath)) return;
const mask = await this.createMask(cpPano.panoId, maskType);
if (!mask) return;
await this.awsS3Manager.uploadBySignedUrl(mask.uploadUrl, cpPano.maskImagePath);
await this.checkUploading(cpPano.panoId, maskType);
await this.updateMaskType(cpPano.panoId, mask.maskId);
}
- Failure point:
aws-s3.manager.ts:93--axios.put가 S3 503 응답을 받아 AxiosError throw
// aws-s3.manager.ts:86-106
async uploadBySignedUrl(signedUrl: string, imagePath: string) {
const agent = new https.Agent({ keepAlive: true });
const fileSize = fs.statSync(imagePath).size;
logger.debug('AwsS3Manager::uploadBySignedUrl | mask image path:%s mask file size:%d KB', imagePath, (fileSize / 1024).toFixed(2));
const fileStream = fs.createReadStream(imagePath);
const response = await axios.put(signedUrl, fileStream, { // <-- 503 발생 지점
httpsAgent: agent,
headers: { 'Content-Type': 'application/octet-stream', 'Content-Length': fileSize }
});
if (response.status !== 200) {
logger.error('AwsS3Manager::uploadBySignedUrl | upload fail - %s %s', imagePath, JSON.stringify(response.statusText));
return;
}
return true;
}
기대 동작: S3 pre-signed URL PUT 요청이 200 OK를 반환하고, 마스크 이미지가 성공적으로 업로드됨.
실제 동작: S3가 503 Service Unavailable을 반환하여 axios.put이 AxiosError를 throw. retry 로직이 없으므로 즉시 실패하고, catch 블록에서 해당 pano를 Error 상태로 마킹.
Log Evidence#
Datadog에서 사용한 쿼리:
service:cupixworks-pano-postprocessor-instance status:error "PanoPostprocessorService::run"
고유 에러 이벤트 4건 (각각 6-7개 filebeat agent에서 중복 수집):
| Timestamp | Pano ID | Job ID | Capture ID | Team | User |
|---|---|---|---|---|---|
| 2026-04-06T22:10:30.942Z | 80244708 | 999045 | 675835 | evansgc | dserrano@evans-gc.com |
| 2026-04-06T22:10:31.329Z | 80244698 | 999045 | 675835 | evansgc | dserrano@evans-gc.com |
| 2026-04-06T22:10:33.464Z | 80244660 | 999045 | 675835 | evansgc | dserrano@evans-gc.com |
| 2026-04-07T03:00:19.886Z | 80289162 | 999714 | 676080 | walmart | danc@jacksondean.com |
첫 3건은 동일 세션(d911c34b23aaa644a3b3fb7a42d92c59fed007a3)에서 약 3초 이내에 발생 -- 동일 capture의 여러 pano에서 동시에 S3 503을 받은 것입니다.
동일 시간대 다른 서비스의 503/S3 에러 (상관관계 확인):
(service:cupixworks-api OR service:cupixworks-worker) status:error ("503" OR "AxiosError")
[2026-04-07T00:08:41.481Z] service=cupixworks-worker class=Record function=flush_geo_coordinate
message: Failed to open TCP connection to s3.me-south-1.amazonaws.com:443 (execution expired)
[2026-04-07T00:31:46.939Z - 00:36:21.224Z] service=cupixworks-worker class=Cupix::VoxelService
message: failed to get captured area - error: 503 Service Unavailable
(10건 이상, 다수 Facility ID에 영향)
[2026-04-07T00:41:55.752Z] service=cupixworks-api class=Cupix::VoxelService function=merge_voxel
message: failed to merge voxels - error: 503 Service Unavailable
이 로그들은 pano-postprocessor의 503 에러와 동일 시간대(2026-04-06 22:10 ~ 2026-04-07 03:00)에 발생하여, AWS 인프라 차원의 일시적 서비스 장애가 있었음을 확인합니다.
pano id 80244708에 대한 warn/info 로그: 검색 결과 0건. 에러 레벨 로그만 존재하며, 업로드 시도 전 debug 로그(파일 크기 등)는 Datadog에서 수집되지 않습니다 (debug 레벨은 Datadog 미수집).
Fix Recommendation#
즉시 조치 (Critical)#
aws-s3.manager.ts:86-106(uploadBySignedUrl메서드): S3 PUT 요청에 exponential backoff retry 로직을 추가해야 합니다. S3 503은 AWS가 공식적으로 retry를 권장하는 transient error입니다.axios-retry라이브러리 또는 수동 retry loop로 최소 3회 재시도(초기 지연 1초, 지수 증가)를 구현해야 합니다.
단기 개선 (1주 이내)#
pano-postprocessor-service.ts:129-138: mask 작업 실패 시 현재는 pano를 즉시 Error 상태로 마킹합니다.uploadBySignedUrl에 retry가 추가되더라도, 전체changeMask호출 레벨에서도 재시도 가능 여부를 검토해야 합니다. 특히 pre-signed URL의 만료 시간을 고려하여, URL 재발급 후 retry하는 로직이 필요할 수 있습니다.- 다른 패키지(
cupix-pix-genie-preprocessor-agent등)의AwsS3Manager에도 동일한 retry 부재 문제가 있는지 확인하고 일괄 수정해야 합니다.
장기 개선 (재발 방지)#
- 공통 HTTP client wrapper에 retry/backoff를 기본 내장하여, 모든 agent 패키지에서 일관된 S3 업로드 resilience를 확보해야 합니다.
- S3 업로드 실패 시 pano를 Error로 마킹하는 대신, 재처리 큐(retry queue)에 넣어 자동 복구하는 메커니즘을 도입할 수 있습니다.
Monitoring#
- S3 signed URL 업로드 실패율 모니터링:
service:cupixworks-pano-postprocessor-instance status:error "uploadBySignedUrl"
- 503 에러 급증 알림 (cross-service):
status:error "503" (service:cupixworks-pano-postprocessor-instance OR service:cupixworks-worker OR service:cupixworks-api)
- pano Error 상태 전환 빈도 추적을 위한 metric 추가 권장
Risk Assessment#
- Risk level: medium
- 예상 복잡도: standard --
uploadBySignedUrl메서드에 retry 로직 추가는 비교적 단순하나, pre-signed URL 만료와의 상호작용 및 다른 패키지 동기화가 필요합니다.