ES /docs

FileSystemManager::loadSkatMasterResults | end - not found file - /efs/440b1b3f3824bb81/skatmaster/r

RCA: FileSystemManager::loadSkatMasterResults | end - not found file

Error Log#

Datadog Logs

text
FileSystemManager::loadSkatMasterResults | end - not found file - /efs/440b1b3f3824bb81/skatmaster/results/processing_status.json

Impact#

  • Service: cupixworks-capture-postprocessor-agent
  • Team: walmart
  • 발생 횟수: 1
  • 최초 발생: 2026-04-14T19:24:22.942Z
  • 최근 발생: 2026-04-14T19:24:22.942Z

Root Cause Summary#

cupixworks-capture-postprocessor-agent가 capture 681052 (job 1015022)의 후처리를 시작했을 때, 상위 단계인 cupix-skat-master-agent가 EFS 경로 /efs/440b1b3f3824bb81/skatmaster/results/processing_status.json 파일을 아직 생성하지 않은 상태였습니다. cupixworks-any-compute-agent가 19:22:47Z에 EFS 경로를 설정하고 컨테이너를 시작한 후 약 95초 만에 postprocessor-agent가 결과를 읽으려 시도했으나, skat-master 처리가 완료되지 않아 파일이 존재하지 않았습니다. 이 에러로 인해 Promise가 reject되었고, getApiErrorToDeleteMessage에서 undefined error로 판단되어 SQS 메시지가 삭제되면서 자동 재시도 기회가 소멸되었습니다.

Technical Analysis#

Code Path#

  • Entry point: base-service.ts:153runByMessage가 SQS 메시지를 수신하여 job 1015022 처리 시작
  • postprocessor-service.ts:64run() 메서드가 job을 로드하고 CPCapture를 생성
  • postprocessor-service.ts:87-91 — EFS 경로 설정 후 순차적으로 파일 로드 시작
typescript
// postprocessor-service.ts:85-91
const fileSystemManager = new FileSystemManager(Environment.DEBUG_MODE);
fileSystemManager.setErrorCode = (errorCode: string) => this.jobManager.setErrorCode(errorCode);
fileSystemManager.setEfsPath(cpCapture);
fileSystemManager.makeEfsDirectory();
await fileSystemManager.loadCPCaptureModel(cpCapture);
await fileSystemManager.loadCPCaptureModelFromVideoDecodingResult(cpCapture);
await fileSystemManager.loadSkatMasterResults(cpCapture);  // <-- 여기서 실패
  • Failure point: file-system.manager.ts:188-190CPUtils.getFileSize()가 -1을 반환하여 파일 미존재 확인 후 reject
typescript
// file-system.manager.ts:178-191
loadSkatMasterResults = (cpCapture: CPCapture): Promise<void> => new Promise((resolve, reject) => {
    logger.debug('FileSystemManager::loadSkatMasterResults | begin');
    if (this.setErrorCode) this.setErrorCode(ErrorCode.Agent.FailedToLoadSkatMasterResults);

    if (this.skatMasterDirPath == undefined) {
        logger.error('FileSystemManager::loadSkatMasterResults | end - undefined directory path');
        return reject();
    }

    const skatMasterResultsFilePath = path.join(this.skatMasterDirPath, Constants.DefaultResultsDirName, Constants.DefaultSkatMasterFileName);
    if (CPUtils.getFileSize(skatMasterResultsFilePath) === -1) {
        logger.error('FileSystemManager::loadSkatMasterResults | end - not found file - %s', skatMasterResultsFilePath);
        return reject();  // reject()에 에러 객체 없이 호출됨
    }
    // ...
  • reject()가 인자 없이 호출되어 에러 값이 undefined로 전파됨
  • base-service.ts:109-110runByMessage에서 throw된 에러가 checkingQueue의 catch 블록으로 전달
typescript
// base-service.ts:106-111
try {
    await this.runByMessages();
} catch (error) {
    await this.handlingMessageErrors(error);
}
  • base-service.ts:240-244getApiErrorToDeleteMessage에서 undefined 에러를 받아 'undefined error' 문자열 반환 (truthy)
typescript
// base-service.ts:240-244
private getApiErrorToDeleteMessage = (error: any): any => {
    if (error == undefined) {
        logger.warn('BaseService::getApiErrorToDeleteMessage | undefined error');
        return 'undefined error';  // truthy 값 반환 → 메시지 삭제 결정
    }
  • base-service.ts:300-304apiErrorObject가 truthy이므로 SQS 메시지가 삭제됨
typescript
// base-service.ts:300-304
const apiErrorObject = this.getApiErrorToDeleteMessage(error);
if (apiErrorObject != undefined || this.checkReceiveCountToDeleteMessage()) {
    // apiErrorObject가 'undefined error'(truthy)이므로 이 블록 실행
    await this.deleteByMessage(this.messageInProcess);
    if (this._modelInProcess != undefined && this._modelInProcess.id > 0) await this.updateErrorState(this._modelInProcess);
}

기대 동작: skat-master 처리가 완료된 후에만 postprocessor-agent가 실행되어야 하며, 파일 미존재 시 재시도가 가능해야 합니다. 실제 동작: skat-master 결과 파일이 아직 생성되지 않은 상태에서 postprocessor가 실행되었고, reject()에 에러 객체가 없어 undefined로 전파되어 getApiErrorToDeleteMessage가 메시지를 삭제 대상으로 판단했습니다.

Log Evidence#

Datadog에서 사용한 쿼리:

text
service:cupixworks-capture-postprocessor-agent status:error @environment:production "FileSystemManager"
text
service:cupixworks-capture-postprocessor-agent @environment:production @job.id:1015022
text
service:cupixworks-any-compute-agent @environment:production "440b1b3f3824bb81"

타임라인 (Job 1015022, Capture 681052):

시간 (UTC) 서비스 레벨 메시지
19:22:47.649Z cupixworks-any-compute-agent info QMJob::makeContainerOverride — EFS 경로 설정 (EFS_SKAT_MASTER_PATH=/efs/440b1b3f3824bb81/skatmaster)
19:24:22.288Z postprocessor-agent info BaseService::runByMessage | id: 1015022
19:24:22.333Z postprocessor-agent info JobManager::loadJob | begin - job id: 1015022
19:24:22.376Z postprocessor-agent info JobManager::loadJob | end - job id: 1015022
19:24:22.927Z postprocessor-agent info CPCapture::fromMeta | key: Y8sm4NF3V, version: 1
19:24:22.942Z postprocessor-agent error FileSystemManager::loadSkatMasterResults | end - not found file - /efs/.../processing_status.json
19:24:22.944Z postprocessor-agent warn BaseService::getApiErrorToDeleteMessage | undefined error
19:24:22.945Z postprocessor-agent info AwsQueueManager::deleteMessage | begin — SQS 메시지 삭제 시작
19:24:22.974Z postprocessor-agent info AwsQueueManager::deleteMessage | end - message id: 95283124-...
19:24:25.631Z postprocessor-agent error BaseService::handlingMessageErrors | Error and message object - {"error":"undefined error","sqsMessage":{"MessageId":"95283124-...","Attributes":{"ApproximateReceiveCount":"1"}}}

핵심 관찰 사항:

  1. ApproximateReceiveCount가 1이므로 첫 번째 시도에서 바로 메시지가 삭제됨 (재시도 없음)
  2. 동일 세션의 다른 capture(681051, job 1015014)는 다른 호스트에서 정상 처리됨 — 인프라 문제가 아닌 타이밍/순서 문제
  3. getApiErrorToDeleteMessageundefined error warn 로그가 에러 객체 없는 reject 확인

Fix Recommendation#

즉시 조치 (Critical)#

  1. file-system.manager.ts:190reject() 호출 시 에러 객체를 포함하도록 수정. 현재 인자 없이 호출되어 undefined가 전파되고, 이것이 getApiErrorToDeleteMessage에서 메시지 삭제로 이어집니다. reject(new Error('...'))로 변경하면 getApiErrorToDeleteMessage가 Node.js 에러로 인식하여 메시지를 삭제하지 않고 재시도 기회를 보존합니다.

  2. base-service.ts:240-244undefined 에러를 받았을 때 메시지를 삭제하는 현재 로직 검토. undefined 에러는 일시적 파일 미존재 등의 원인일 수 있으므로, 재시도가 더 적절한 처리 방식입니다.

단기 개선 (1주 이내)#

  1. 파이프라인 순서 보장: cupixworks-any-compute-agent가 postprocessor SQS 메시지를 보내기 전에 skat-master 처리가 완료되었는지 확인하는 메커니즘 검토. 현재 약 95초 만에 postprocessor가 실행을 시도하는데, skat-master 처리 시간이 이보다 길 수 있습니다.

  2. 재시도 로직 추가: loadSkatMasterResults에서 파일 미존재 시 일정 간격으로 재시도하는 로직 추가 검토 (예: 30초 간격으로 3회 재시도). 다른 load 메서드들(loadCPCaptureModelFromVideoDecodingResult 등)은 파일 미존재를 resolve로 처리하는데, loadSkatMasterResults는 reject하여 일관성이 없습니다.

장기 개선 (재발 방지)#

  1. 이벤트 기반 트리거링: postprocessor-agent가 SQS 폴링이 아닌 skat-master 완료 이벤트를 기반으로 실행되도록 아키텍처 변경 검토. 이렇게 하면 처리 순서가 보장됩니다.

  2. reject() 표준화: 모든 agent 패키지에서 reject() 호출 시 항상 에러 객체를 포함하도록 코딩 표준 적용. getApiErrorToDeleteMessage가 에러 타입에 따라 재시도/삭제를 결정하므로, 에러 객체 누락은 잘못된 삭제 판단으로 이어집니다.

Monitoring#

  • 다음 Datadog 쿼리로 동일 패턴 모니터링:
text
service:cupixworks-capture-postprocessor-agent status:error "loadSkatMasterResults" "not found file"
  • undefined error로 인한 SQS 메시지 삭제 추적:
text
service:cupixworks-capture-postprocessor-agent "getApiErrorToDeleteMessage" "undefined error"
  • ApproximateReceiveCount:1에서 삭제되는 메시지 비율 메트릭 추가 권장

Risk Assessment#

  • Risk level: medium
  • 예상 복잡도: standard