ES /docs

BaseService::handlingMessageErrors | sqsMessage - {"MessageId":"29feac74-a0ed-4837-bd3b-ec22ee9b6cdd

RCA: BaseService::handlingMessageErrors ENOSPC (capture-preprocessor-agent)

Overview#

capture-preprocessor-agent 가 특정 capture 의 video 를 전처리하다가 컨테이너의 /tmp 디스크가 가득 차 ENOSPC: no space left on device 로 실패했다. 실패 지점은 checkSupportedVideo 가 이미 다운로드된 원본 video 를 검증용 임시 폴더로 다시 복사하는 fs.copyFileSync 단계다. 이 에러는 SQS 시스템 에러로 분류되어 메시지가 삭제되지 않고, ApproximateReceiveCount 가 1 에서 9 까지 반복 재시도되며 매번 같은 지점에서 실패했다.

What Happened#

2026-08-06, production 의 capture-preprocessor-agent 가 team southlandind 의 video capture (job 749769, capture 708445) 를 처리하던 중 /tmp/workspace 디스크 부족으로 원본 .insv 파일 복사에 실패했다. 실패한 message 는 SQS 로 반환되어 9 회까지 재처리되었고, 매 재시도마다 동일한 ENOSPC 로 종료됐다. 같은 원인의 실패가 14일 창에서 4개의 서로 다른 capture 에 걸쳐 138건 관측됐다.

Quick Facts#

Field Value
exception.class Error (Node.js system error, code: "ENOSPC", errno: -28)
exception.message ENOSPC: no space left on device, copyfile '/tmp/workspace/749769/videos/708445/original/708445.insv' -> '/tmp/workspace/749769/supported_videos/VID_20260803_094123_00_004.insv'
top_frame PreprocessorService.checkSupportedVideo (preprocessor-service.ts:846 fs.copyFileSync)
syscall copyfile
runtime Node.js agent, workspace /tmp/workspace (컨테이너 ephemeral disk)
env production, us-west-2

Affected Teams#

Team / Domain Error Count Impact
southlandind 138 ENOSPC 이벤트 (14d, 4 captures) 해당 video capture 들의 preprocessor action 이 완료되지 못하고 반복 실패

에러 로그에서 확인된 team 은 southlandind 하나이며, 실패한 capture 는 706551, 706595, 707510, 708445 4개다.

Timeline#

  1. 2026-08-06 19:17 KST — job 749769 message 재시도 시작 (ApproximateReceiveCount:1), checkSupportedVideo 에서 첫 ENOSPC 발생.
  2. 2026-08-06 19:57 KST — 클러스터 대표 로그 기록 (ApproximateReceiveCount:9), 동일 지점에서 반복 실패.
  3. 2026-08-06 19:57 KST — first_seen = last_seen (collector 가 이 시점 샘플을 클러스터로 수집).

Error Log#

Datadog Logs

text
BaseService::handlingMessageErrors | sqsMessage - {"MessageId":"29feac74-a0ed-4837-bd3b-ec22ee9b6cdd","Attributes":{"ApproximateReceiveCount":"9"}}, error:

대표 로그의 error: 뒤가 비어 있는 이유는 아래 Root Cause Summary 및 H2 에서 설명한다. 같은 message 의 실제 에러 내용은 인접 로그에 온전히 남아 있다.

Impact#

  • Service: cupixworks-capture-preprocessor-agent
  • Team: southlandind
  • 발생 횟수: 1 (클러스터 기준) / 138 ENOSPC 이벤트 (14d 로그 실측)
  • 최초 발생: 2026-08-06 19:57 KST
  • 최근 발생: 2026-08-06 19:57 KST

Root Cause Summary#

capture-preprocessor-agent 는 capture 의 원본 video 를 /tmp/workspace/{job}/videos/{video}/original/ 로 다운로드한 뒤, checkSupportedVideo 단계에서 검증 도구에 넘기기 위해 같은 원본을 /tmp/workspace/{job}/supported_videos/ 로 한 번 더 fs.copyFileSync 복사한다. 이 복사가 원본 크기만큼의 디스크를 추가로 요구하는데, .insv (Insta360) 원본이 큰 경우 컨테이너 ephemeral /tmp 용량을 초과해 ENOSPC: no space left on device 로 실패한다. 이 에러는 HTTP status 가 없는 Node.js system error 라서 getApiErrorToDeleteMessageundefined 를 반환한다(system error 조기 반환). 그 결과 message 는 ApproximateReceiveCountMaxReceiveCount(10) 에 도달하기 전까지 SQS 로 계속 반환되어, 같은 capture 가 매 재시도마다 동일 지점에서 실패하는 결정론적 반복 실패가 된다. 즉 코드가 큰 video 를 위한 디스크 여유를 확보하지 못한 리소스 결함이며, 재시도가 문제를 해소하지 못한다.

Technical Analysis#

Code Path#

  • Entry point: base-service.ts:108checkingQueuerunByMessages 를 호출, 예외 시 handlingMessageErrors 로 위임.
  • 실행 흐름: runByMessage (base-service.ts:153) → PreprocessorService.run (preprocessor-service.ts:93) → video 다운로드 (downloadVideoFiles, preprocessor-service.ts:436) → checkSupportedVideo (preprocessor-service.ts:824).
  • Failure point: preprocessor-service.ts:846 — 다운로드된 원본을 supported_videos/ 로 복사하는 fs.copyFileSync.

run 은 원본 video 다운로드 후 검증을 수행한다.

applications/agents/packages/cupix-capture-preprocessor-agent/src/preprocessor-service.ts:121-145typescript
			await this.downloadVideoFiles(cpCapture);
			...
			await this.loadEntityParameter(cpCapture);
			this.checkCameraModel(cpCapture);
			this.setScenemapperOptions(cpCapture);
			await this.checkSupportedVideo(cpCapture);

checkSupportedVideo 는 이미 로컬에 있는 각 원본 video (it.path) 를 검증용 임시 폴더로 다시 복사한다. 이 시점에 원본과 사본이 동시에 디스크를 점유한다.

applications/agents/packages/cupix-capture-preprocessor-agent/src/preprocessor-service.ts:844-848typescript
		cpCapture.cpVideos.forEach(it => {
			if (it.path) {
				fs.copyFileSync(it.path, path.join(inputDirPath, it.name));
			}
		});

workspace 는 컨테이너의 ephemeral /tmp 이다.

applications/agents/packages/shared-config/src/constants.ts:21typescript
export const DefaultWorkspacePath = '/tmp/workspace';

에러를 받은 handlingMessageErrorsgetApiErrorToDeleteMessage 로 삭제 여부를 판단한다. Node.js system error (errno/code/syscall 필드 존재) 는 조기에 undefined 를 반환한다.

applications/agents/packages/base/src/base-service.ts:245-247typescript
		if (error.errno != undefined && error.code != undefined && error.syscall != undefined) {
			logger.warn('BaseService::getApiErrorToDeleteMessage | nodejs common system error', error);
			return;
		}

apiErrorObjectundefined 이므로 메시지 삭제는 오직 checkReceiveCountToDeleteMessagetrue 일 때, 즉 ApproximateReceiveCount >= MaxReceiveCount(10) 일 때만 일어난다. 그 전까지는 message 가 SQS 로 반환되어 재처리된다.

applications/agents/packages/base/src/base-service.ts:283-287typescript
		if (receiveCount == undefined) return true;

		if (receiveCount >= Constants.MaxReceiveCount) return true;

		return false;

MaxReceiveCount 는 10 이다.

applications/agents/packages/shared-config/src/constants.ts:11typescript
export const MaxReceiveCount = 10;

기대 동작: 지원 video 검증을 위해 임시 복사가 필요하다면, 복사 전에 여유 디스크를 확인하거나 복사를 피하고(예: 원본 경로를 검증 도구에 직접 넘김) 실패 시 재시도가 무의미한 리소스 에러를 조기 종료해야 한다. 실제 동작: 원본 크기만큼 복사를 시도해 /tmp 를 넘기고, 재시도가 같은 지점에서 반복 실패한다.

Log Evidence#

사용한 Datadog 쿼리:

text
service:cupixworks-capture-preprocessor-agent "29feac74-a0ed-4837-bd3b-ec22ee9b6cdd"
text
service:cupixworks-capture-preprocessor-agent "ENOSPC"

대표 로그와 같은 message 의 인접 로그(ApproximateReceiveCount:8)에 실제 에러 payload 가 온전히 남아 있다.

json
{
  "timestamp": "2026-08-06 17:57:30",
  "status": "error",
  "message": "BaseService::handlingMessageErrors | Error and message object - {\"error\":{\"errno\":-28,\"code\":\"ENOSPC\",\"syscall\":\"copyfile\",\"path\":\"/tmp/workspace/749769/videos/708445/original/708445.insv\",\"dest\":\"/tmp/workspace/749769/supported_videos/VID_20260803_094123_00_004.insv\"},\"sqsMessage\":{\"MessageId\":\"29feac74-a0ed-4837-bd3b-ec22ee9b6cdd\",\"Attributes\":{\"ApproximateReceiveCount\":\"8\"}}}"
}

full stack trace 가 담긴 warn 로그가 실패 지점을 checkSupportedVideocopyFileSync 로 확정한다.

text
CupixAuth::handleError | Undefined response: {"stack":"Error: ENOSPC: no space left on device, copyfile '/tmp/workspace/748923/videos/707510/original/707510.insv' -> '/tmp/workspace/748923/supported_videos/VID_20260803_094123_00_004.insv'
    at Object.copyFileSync (node:fs:3047:11)
    ...
    at PreprocessorService.checkSupportedVideo (/tmp/agent/dist/app.cjs:9919:26)
    at PreprocessorService.run (/tmp/agent/dist/app.cjs:9312:20)"}

같은 message (29feac74...) 가 ApproximateReceiveCount 1 → 9 로 반복되며 매 재시도마다 setVideoImageMatchData | begin - capture_id: 749769 직후 동일 ENOSPC 로 종료된다. 결정론적 재시도 실패다.

14일 창에서 ENOSPC 는 138건 발생했고, 서로 다른 4개 capture 로 나뉜다.

text
/tmp/workspace/747932/videos/706551
/tmp/workspace/747973/videos/706595
/tmp/workspace/748923/videos/707510
/tmp/workspace/749769/videos/708445

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 원본 video 를 supported_videos/ 로 다시 복사하는 checkSupportedVideofs.copyFileSync/tmp ephemeral 디스크를 초과해 ENOSPC 발생 stack trace 최상단 Object.copyFileSyncPreprocessorService.checkSupportedVideo (preprocessor-service.ts:846); error payload code:ENOSPC errno:-28 syscall:copyfile path=.../original/708445.insv dest=.../supported_videos/...; workspace /tmp/workspace (constants.ts:21) Confirmed
H2 대표 로그의 error: 가 비어 있는 것은 별도 에러가 아니라 system-error 분류로 errorAndMessage.errorundefined 로 덮여 로깅된 것 getApiErrorToDeleteMessage system error 조기 return(base-service.ts:245-247); handlingMessageErrorserrorAndMessage.error = apiErrorObject 로 대입(base-service.ts:303); 같은 message 의 인접 로그에 full ENOSPC payload 존재 Confirmed
H3 특정 capture 의 손상/비지원 video 로 인한 결정론적 애플리케이션 에러 (디스크와 무관) 같은 capture 가 매 재시도 실패 에러가 ENOSPC 시스템 에러이며 파일 검증 로직 이전 복사 단계에서 발생; 4개의 서로 다른 capture 에서 동일 발생 → capture 데이터가 아니라 디스크 리소스 문제 Rejected
H4 외부 의존성 (S3/SQS) 장애 ApproximateReceiveCount 반복 status-board svc:cupixworks-capture-preprocessor-agent::unknown active/recent 모두 없음; 에러 payload 가 로컬 filesystem ENOSPC 로 명확 Rejected
H5 SQS message 삭제 실패로 무한 재시도 재시도 1 → 9 관측 MaxReceiveCount(10) 도달 시 삭제 로직 정상(base-service.ts:285) — 무한이 아니라 10회 상한, 설계된 재시도 소진 Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • 대상: applications/agents/packages/cupix-capture-preprocessor-agent/src/preprocessor-service.ts:844-848 (checkSupportedVideo), 동일 패턴이 checkLicenseValidity preprocessor-service.ts:893-897 에도 존재.
  • 방향: 원본 video 를 supported_videos/ 로 전체 복사하는 대신 복사를 제거하고 검증 도구에 원본 경로를 직접 전달하거나 심볼릭 링크(fs.symlinkSync)로 대체하는 방향을 검토한다. 검증 후 deleteFolderRecursive 로 정리하지만, 복사 시점에 원본 크기만큼의 여유가 없으면 실패하므로 복사 자체를 줄이는 것이 근본 대응이다. 검증 도구의 입력 형식 제약이 있어 복사가 불가피하면, 복사 전 checkAndCreateFolder(inputDirPath) 직후 여유 공간(fs.statfs)을 확인하고 부족 시 재시도 무의미한 리소스 에러로 조기 실패시킨다.
  • 병행: ENOSPC 같은 디스크 리소스 에러는 SQS 재시도로 해소되지 않으므로, handlingMessageErrors 경로에서 시스템 리소스 에러(code === 'ENOSPC' 등)를 재시도 불가로 판단해 조기 정리/에러 상태 전이하도록 분류를 좁힌다 (base-service.ts:290-313, 프런트/운영 계약 영향 없으므로 agent-side 처리).

단기 개선 (1주 이내)#

  • capture-preprocessor-agent 의 ECS task 정의에서 큰 .insv (Insta360) 원본과 그 임시 사본을 동시에 수용하도록 ephemeral storage 용량을 상향한다. 현재 /tmp/workspace 가 원본 + 사본을 담지 못한다는 것이 로그로 확인됐다.
  • video 다운로드 완료 직후, 검증/전처리 단계 진입 전에 사용 가능 디스크와 원본 총 크기를 비교해 부족 시 명시적 에러 코드로 종료하도록 가드를 추가한다.

장기 개선 (재발 방지)#

  • 전처리 파이프라인에서 대용량 미디어를 여러 번 물리 복사하는 지점을 전수 조사해 (downloadVideoFiles, checkSupportedVideo, checkLicenseValidity) 스트리밍 또는 참조 전달로 전환한다.
  • agent 컨테이너에 디스크 사용률 메트릭을 emit 하고, 특정 임계치 초과 시 스케줄러가 큰 job 을 여유 있는 인스턴스로 배치하도록 한다.

Monitoring#

ENOSPC 실패 발생 추이 (fix 배포 후 0 으로 수렴해야 함):

text
service:cupixworks-capture-preprocessor-agent status:error "ENOSPC"

checkSupportedVideo 복사 실패 로그 추이:

text
service:cupixworks-capture-preprocessor-agent "no space left on device" "copyfile"

handlingMessageErrors 최종 에러 로그 추이 (agent 처리 실패 전반):

text
service:cupixworks-capture-preprocessor-agent status:error "handlingMessageErrors"

Risk Assessment#

  • Risk level: medium — 데이터 손상은 없으나 해당 team 의 큰 video capture 전처리가 완료되지 못하고 재시도를 소진한 뒤 error 상태로 종료된다.
  • 예상 복잡도: standard — 복사 제거/여유 확인 가드 또는 ephemeral storage 상향 중 선택. 검증 도구의 입력 형식 제약 확인이 필요.