ES /docs

forge_bimmodel_extractor process() blocked on Forge API call — model-specific hang

RCA: ChildProcessManager::sendMessage | Execution timeout after 28800000ms for message type: execute

Overview#

What Happened#

2026-04-30에 cupixworks-any-mesh-agent 서비스에서 BIM 모델 mesh 추출 작업 2건이 8시간(28800000ms) 실행 제한 시간을 초과하여 timeout 에러가 발생했다. BIM ID 19229 (us-west-2)와 BIM ID 1291 (eu-central-1)이 영향을 받았으며, 동일 시간대 다른 BIM 작업들은 수 분 내에 정상 완료되었다.

Quick Facts#

Field Value
exception.class Error
exception.message Execution timeout after 28800000ms for message type: execute
top_frame /tmp/agent/dist/app.cjs:1593
env production, us-west-2 / eu-central-1

Affected Teams#

Team / Domain Error Count Impact
qatest3 2 BIM 모델 2건의 mesh 추출 실패, mesh_state가 error로 전환됨

Timeline#

  1. 2026-04-30 13:43:57 KST — BIM 19229 mesh 추출 작업 시작 (us-west-2 SQS 큐)
  2. 2026-04-30 13:54:27 KST — BIM 1291 mesh 추출 작업 시작 (eu-central-1 SQS 큐)
  3. 2026-04-30 21:43:59 KST — BIM 19229 timeout 발생 (8시간 경과), child process SIGKILL
  4. 2026-04-30 21:54:29 KST — BIM 1291 timeout 발생 (8시간 경과), child process SIGKILL
  5. 2026-04-30 21:54:29 KST — 양쪽 모두 cleanup 후 SQS 메시지 삭제 완료

Error Log#

Datadog Logs

text
ChildProcessManager::sendMessage | Execution timeout after 28800000ms for message type: execute

Impact#

  • Service: cupixworks-any-mesh-agent
  • Team: qatest3
  • 발생 횟수: 2
  • 최초 발생: 2026-04-30T12:43:59.764Z
  • 최근 발생: 2026-04-30T12:54:29.724Z

Root Cause Summary#

forge_bimmodel_extractorprocess() 메서드가 동기적(synchronous)으로 실행되는 C++ 네이티브 바인딩인데, 특정 BIM 모델(ID 19229, 1291)에 대해 Autodesk Forge API 호출 또는 mesh 데이터 처리 중 무한 대기(hang) 상태에 빠졌다. ChildProcessManagerexecuteTimeoutMs(8시간 = 28800000ms) 타이머를 설정하여 이 상황을 감지하고 child process를 SIGKILL로 강제 종료했다. 근본 원인은 child process 내부의 extractBIMModel.process() 호출이 반환되지 않은 것이며, 이는 해당 BIM 모델의 Forge URN에 대한 다운로드/파싱 과정에서 네이티브 코드가 블로킹된 것으로 추정된다. 동일 시간대에 10건 이상의 다른 BIM 모델이 수 분 내에 정상 추출 완료된 점으로 볼 때, 모델 특이적 문제이다.

Technical Analysis#

Code Path#

  • Entry point: mesh-service.ts:101MeshService.run(targetId)가 SQS 메시지에서 BIM ID를 받아 처리 시작
  • mesh-service.ts:112this.runMeshExtractor(cpBim) 호출
  • mesh-service.ts:184this.meshExtractManager.execute(params) 호출, timeOut: Environment.CPX_EXTRACTOR_TIMEOUT 전달
packages/cupix-tesla-mesh-agent/src/mesh-service.ts:54-57typescript
this._meshExtractManager = new MeshExtractManager({
    stdioMode: 'pipe',
    executeTimeoutMs: Environment.CPX_EXTRACTOR_TIMEOUT,
});
  • mesh-extract.manager.ts:70this.childProcessManager.execute<MeshExtractorResults>('execute', params) 호출
  • child-process.manager.ts:186-198execute() 메서드에서 effectiveTimeoutthis.options.executeTimeoutMs에서 가져옴
packages/base/src/manager/child-process.manager.ts:186-198typescript
async execute<T = unknown>(type: string, data?: unknown, timeoutMs?: number): Promise<T> {
    if (!this.isRunning) {
        await this.start();
    }

    const message: Message = {
        id: randomUUID(),
        type,
        data
    };

    const effectiveTimeout = timeoutMs ?? this.options.executeTimeoutMs;
    return this.sendMessage(message, effectiveTimeout);
}
  • child-process.manager.ts:213-225sendMessage()에서 setTimeout으로 timeout 타이머 설정, timeout 시 child process를 SIGKILL로 종료
packages/base/src/manager/child-process.manager.ts:213-225typescript
if (timeoutMs && timeoutMs > 0) {
    pending.timer = setTimeout(() => {
        this.pendingMessages.delete(message.id);
        const error = new Error(`Execution timeout after ${timeoutMs}ms for message type: ${message.type}`);
        logger.error(`ChildProcessManager::sendMessage | ${error.message}`);

        if (this.process && !this.process.killed) {
            logger.warn('ChildProcessManager::sendMessage | Killing child process due to timeout');
            this.process.kill('SIGKILL');
        }

        reject(error);
    }, timeoutMs);
}
  • Failure point: mesh-extractor.process.ts:138 — child process 내 extractBIMModel.process() 동기 호출이 반환되지 않음
packages/cupix-tesla-mesh-agent/src/process/mesh-extractor.process.ts:129-150typescript
const extractBIMModel = new BIM_MODEL_EXTRACTOR.extract_bimmodel();

const envName = CPX_ENVIRONMENT_NAME?.toLowerCase();

this.log(`MeshExtractorProcess::execute - Environment: ${envName}`);

extractBIMModel.setServer(envName);
extractBIMModel.setModelUrn(params.urn);
extractBIMModel.setOutputDirpath(params.path);
const result = extractBIMModel.process();

this.log(`MeshExtractorProcess::execute - process() result: ${result} (${typeof result})`);

if (result === true) {
    const fileList = await CPUtils.getFiles(params.path);
    this.log(`MeshExtractorProcess::execute - extracted ${fileList.length} files: ${JSON.stringify(fileList)}`);
    extract = {
        filepaths: fileList
    };
} else {
    this.log(`MeshExtractorProcess::execute - extraction failed, result: ${result}`);
    throw new Error(`forge_bimmodel_extractor failed to extract mesh, process() returned: ${result}`);
}
  • Timeout 값은 constants.ts:6에서 하드코딩:
packages/cupix-tesla-mesh-agent/src/config/constants.ts:6typescript
export const CupixMeshExtractorTimeOut = 28800000; // ms (8 hours)

기대 동작: extractBIMModel.process()가 mesh 데이터를 추출하고 true/false를 반환해야 한다. 정상적인 경우 1~3분 내에 완료된다 (동일 시간대 다른 작업들은 runByMessage 후 수 분 내에 fromMeshExtractor | filepaths count: 16이 로깅됨).

실제 동작: 특정 BIM 모델에 대해 process() 메서드가 반환되지 않고 8시간 동안 블로킹됨. ChildProcessManager의 timeout 타이머가 만료되어 SIGKILL로 child process를 강제 종료함.

Log Evidence#

사용한 Datadog 쿼리:

text
service:cupixworks-any-mesh-agent (19229 OR 1291)

BIM 19229 타임라인:

text
2026-04-30 13:43:57 KST | info | BaseService::runByMessage | id: 19229
2026-04-30 21:43:59 KST | error | ChildProcessManager::sendMessage | Execution timeout after 28800000ms for message type: execute
2026-04-30 21:43:59 KST | warn  | ChildProcessManager::sendMessage | Killing child process due to timeout
2026-04-30 21:43:59 KST | error | MeshService::run | error: {"stack":"Error: Execution timeout after 28800000ms for message type: execute\n    at Timeout._onTimeout (/tmp/agent/dist/app.cjs:1593:29)...","message":"Execution timeout after 28800000ms for message type: execute"}
2026-04-30 21:43:59 KST | error | ChildProcessManager::setupEventHandlers | Child process exited
2026-04-30 21:43:59 KST | error | ChildProcessManager::setupEventHandlers | Child process closed
2026-04-30 21:44:00 KST | info  | BaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/19229
2026-04-30 21:44:00 KST | info  | AwsQueueManager::deleteMessage | begin - queue url: https://sqs.us-west-2.amazonaws.com/002596530511/cupix-tesla-mesh-agent-production

BIM 1291 타임라인:

text
2026-04-30 13:54:27 KST | info | BaseService::runByMessage | id: 1291
2026-04-30 21:54:29 KST | error | ChildProcessManager::sendMessage | Execution timeout after 28800000ms for message type: execute
2026-04-30 21:54:29 KST | warn  | ChildProcessManager::sendMessage | Killing child process due to timeout
2026-04-30 21:54:29 KST | error | MeshService::run | error: {"stack":"Error: Execution timeout after 28800000ms for message type: execute..."}
2026-04-30 21:54:29 KST | info  | BaseService::cleanUpAnythingRelatedModel | path: /tmp/workspace/1291
2026-04-30 21:54:29 KST | info  | AwsQueueManager::deleteMessage | begin - queue url: https://sqs.eu-central-1.amazonaws.com/002596530511/cupix-tesla-mesh-agent-production

정상 완료된 작업 비교 (동일 시간대):

text
service:cupixworks-any-mesh-agent "fromMeshExtractor"
text
2026-04-30 13:42:35 KST | info | CPBim::fromMeshExtractor | filepaths count: 16
2026-04-30 13:43:53 KST | info | CPBim::fromMeshExtractor | filepaths count: 20
2026-04-30 13:43:55 KST | info | CPBim::fromMeshExtractor | filepaths count: 16
2026-04-30 13:44:01 KST | info | CPBim::fromMeshExtractor | filepaths count: 16
2026-04-30 13:44:09 KST | info | CPBim::fromMeshExtractor | filepaths count: 16

14일간 전체 Execution timeout 에러:

text
service:cupixworks-any-mesh-agent status:error "Execution timeout"

14일 내 발생 건수: 이 2건의 에러만 존재 (BIM 19229, 1291). 이전에는 동일 패턴 없음.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 특정 BIM 모델의 Forge URN이 대용량이거나 비정상적 구조여서 forge_bimmodel_extractor C++ 네이티브 코드가 무한 대기 동일 시간대 다른 BIM 작업들은 1-3분 내 정상 완료. BIM 19229, 1291만 8시간 동안 응답 없이 hang. fromMeshExtractor 로그가 없음. child process 내부 로그가 없어 정확한 hang 지점 확인 불가 (native code) Confirmed
H2 Autodesk Forge API 전체 장애로 인한 타임아웃 두 건 모두 같은 날 발생 동일 시간대(13:41~13:45 KST)에 시작된 다른 BIM 작업 10건 이상이 정상 완료됨. us-west-2와 eu-central-1 양쪽 리전에서 발생하여 Forge API 리전 장애도 아님 Rejected
H3 child process 메모리 부족(OOM)으로 인한 hang stdioMode: 'pipe'로 설정되어 있어 메모리 이슈 가능성 존재 OOM이면 OS가 kill하며 Child process exited 로그가 즉시 발생해야 하나, 실제로는 8시간 후 timeout에 의한 SIGKILL로 종료됨 Rejected
H4 네트워크 파티션/DNS 이슈로 Forge API 연결 hang C++ 네이티브 코드가 내부적으로 HTTP 요청 시 무한 대기할 수 있음 모델 특이적 패턴과 일치하며, Forge API 자체의 문제보다는 특정 모델 처리 시 네이티브 코드의 동작이 원인 Inconclusive

Fix Recommendation#

즉시 조치 (Critical)#

  • constants.ts:6CupixMeshExtractorTimeOut을 8시간에서 더 합리적인 값(예: 1-2시간)으로 줄이는 것을 검토. 정상 작업이 수 분 내에 완료되므로 8시간은 과도한 timeout.
  • 해당 BIM 모델(ID 19229, 1291)의 상태를 확인하고, 필요 시 mesh_state를 수동으로 리셋하여 재처리 또는 수동 검증.

단기 개선 (1주 이내)#

  • mesh-extractor.process.ts에서 extractBIMModel.process() 호출 전후로 시작 시각과 BIM URN을 info 레벨로 로깅하여, 어떤 모델에서 long-running 상태가 발생하는지 추적 가능하도록 개선.
  • child process 내부에서 자체적인 timeout 메커니즘 도입 검토 (예: process() 호출을 별도 worker thread에서 실행하고 AbortSignal 등으로 제어).

장기 개선 (재발 방지)#

  • forge_bimmodel_extractor C++ 네이티브 모듈에 내부 timeout 파라미터를 추가하여, 네이티브 레벨에서 HTTP 요청이나 데이터 처리에 대한 개별 timeout을 설정할 수 있도록 개선.
  • extractBIMModel.process()가 동기적으로 블로킹하는 대신 비동기 인터페이스(callback 또는 Promise)를 제공하도록 네이티브 모듈 업데이트 검토.
  • 대용량/비정상 BIM 모델에 대한 사전 검증 로직 추가 (예: Forge 모델 메타데이터에서 크기/복잡도를 확인하고 임계치 초과 시 거부).

Monitoring#

  • timeout 발생 시 즉시 알림을 받을 수 있도록 Datadog monitor 추가:
text
service:cupixworks-any-mesh-agent status:error "Execution timeout"
  • mesh 추출 소요 시간을 추적하는 커스텀 메트릭 도입 검토 (start ~ fromMeshExtractor 간 시간 차이)
  • forge_bimmodel_extractor.process() 실행 시간이 30분을 초과하면 warn 로그를 남기는 중간 체크포인트 추가 검토

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: standard
  • 14일 내 2건만 발생한 저빈도 이벤트. 정상적인 error handling flow가 동작하여 BIM mesh_state를 error로 업데이트하고 SQS 메시지를 삭제했으므로, 시스템 안정성에 대한 영향은 제한적. 다만 8시간 동안 agent 리소스를 점유하는 것은 비효율적이며, timeout 값 조정이 권장됨.