ES /docs

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#

  1. 14:35:25Z — 첫 번째 timeout 발생 (job 101985, capture 36961)
  2. 14:41:42Z — ECONNRESET 발생 (job 102001, capture 36963)
  3. 15:28:54Z — job 102003에서 timeout 발생
  4. 15:49:55Z — 이 클러스터의 첫 번째 에러 (job 102003, capture 36966)
  5. 16:03:34Z — 이 클러스터의 두 번째 에러 (동일 세션)
  6. 16:11:31Z — S3 InternalError 발생 (동일 세션)
  7. 16:17:04Z — job 102003 정상 완료 (304/304 panos, 0 errored)

Error Log#

Datadog Logs

text
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:141uploadResizedImage 호출
  • Failure point: aws-s3.manager.ts:131-139 — S3 upload 시 timeout 발생

S3 클라이언트 생성 시 retry/timeout 설정이 없음:

applications/agents/packages/cupix-pano-postprocessor/src/manager/aws-s3.manager.ts:109-119typescript
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:

applications/agents/packages/cupix-pano-postprocessor/src/manager/aws-s3.manager.ts:131-140typescript
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 설정이 있음:

applications/agents/packages/base/src/manager/aws-s3.manager.tstypescript
AWS.config.update({
    region: __region,
    maxRetries: 5,
    retryDelayOptions: { base: 200 }
});

상위 orchestrator에서 파노 단위 catch가 있어 전체 파이프라인은 보호됨:

applications/agents/packages/cupix-pano-postprocessor/src/service/pano-postprocessor-service.ts:165-178typescript
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 검색 쿼리:

text
service:cupixworks-pano-postprocessor-instance status:error @environment:production "me-central-1"

동일 세션에서 발생한 전체 에러 로그:

json
{"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}
json
{"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}
json
{"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):

text
PanoPostprocessorService Metric - dd_metric:pano.postprocess.summary&dd_total:304&dd_completed:304&dd_errored:0

동일 시간대 다른 서비스의 me-central-1 접근 성공 (16:21~16:23Z):

text
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 빈도 모니터링:
text
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