ChildProcessManager::setupEventHandlers | Child process stderr: 16: 0x12cbb55 v8::internal::ArrayCon
RCA: BimCompareProcess child killed by V8 OOM during BIM V4→V5 comparison
Overview#
What Happened#
cupixworks-any-bimrevision-agent production instance가 bim_id 17216 (IAD157 Coordination Model, V4 → V5) revision 을 처리하던 중, BimCompareProcess child process 가 heap 약 7.9 GB 에서 V8 FATAL ERROR: Ineffective mark-compacts near heap limit 로 SIGABRT 종료되었다. 부모 ChildProcessManager 는 stderr pipe 로 흘러들어온 V8 crash native stack trace 라인 각각을 logger.error(...) 로 기록하여 25개의 개별 error cluster 로 파편화되었다. 사용자 영향은 해당 revision 작업 1건 실패 (elapsed 883 s).
Quick Facts#
| Field | Value |
|---|---|
| exception.class | V8 FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory |
| top_frame | v8::internal::ArrayConstructInitializeElements → v8::internal::Runtime_NewArray |
| child stderr sink | applications/agents/packages/base/src/manager/child-process.manager.ts:138-141 |
| child entry | applications/agents/packages/cupix-tesla-bim-revision-agent/src/process/bim-compare.process.ts:67 |
| child heap limit | --max-old-space-size=8192 (inherited from parent, docker_services/80_run-agent.sh:3) |
| bim_id | 17216 |
| src_revision_id / prev_revision_id | 25433 (V5) / 21647 (V4) |
| query size | entities=154331, checkExistence=57346, cpElements total=211677 |
| env | production, us-west-2, tenant=cupix, team_domain=clark-vdc |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
clark-vdc (BIM revision, bim_id 17216) |
1 job failed (25개 error 로그 클러스터로 파편화) | V4→V5 revision 결과 미생성; 사용자는 재시도 필요 |
Timeline#
- 2026-07-21 20:47:02 KST —
BaseService::runByMessage시작,si_trace_id: a052780a-c69b-457d-b72d-3aa6332272a3 - 2026-07-21 20:52:10 KST —
BimRevisionService::loadRemovedElements | begin - bimId: 17216 - 2026-07-21 20:53:08 KST —
loadAllElements | end - 211677 cpElements loaded - created: 154331, deleted: 57346 - 2026-07-21 20:53:10 KST —
REVISE-BEGIN(V4→V5),runBimCompare | begin - 2026-07-21 20:53:11 KST — child forked, forge-agents
extract호출 시작 (query.entities=154331) - 2026-07-21 21:00:39 KST — child V8 Mark-Compact 두 차례 실패 (7909.6 MB → 7916.1 MB, heap cap 8231 MB),
FATAL ERROR: Ineffective mark-compacts near heap limit, native stack dump (frames 1..18) 를 stderr 로 flush - 2026-07-21 21:01:45 KST — parent 가 child close/exit 수신,
Process killed by signal SIGABRT,BimRevisionService::run | error - 2026-07-21 21:01:46 KST —
REVISE-END status=error, elapsed_ms=883421,/tmp/workspace/25433정리, SQS 메시지 삭제
Error Log#
ChildProcessManager::setupEventHandlers | Child process stderr: 16: 0x12cbb55 v8::internal::ArrayConstructInitializeElements(v8::internal::Handle<v8::internal::JSArray>, v8::internal::Arguments<(v8::internal::ArgumentsType)1>*) [/usr/bin/node]
Impact#
- Service:
cupixworks-any-bimrevision-agent - Team: clark-vdc
- 발생 횟수: 1 (본 클러스터). 동일 crash 로부터 파생된 관련 클러스터 24개가 status-board 인시던트
2026-07-21-svc-cupixworks-any-bimrevision-agent--unknown-1로 묶여 있음 - 최초 발생: 2026-07-21 21:00:39 KST
- 최근 발생: 2026-07-21 21:00:39 KST
Root Cause Summary#
BIM V4→V5 비교 작업이 query.entities.length: 154331, checkExistence.length: 57346, 총 cpElements: 211677 규모로 forge-agents ForgeAgent.extract 에 전달되어 child process 에서 실행되었다. child 는 부모의 --max-old-space-size=8192 를 상속받아 old-space 상한 ~8 GB 로 동작했지만, forge-agents CompareExtractor.compare / GridAlignment.computeGridCorrection 경로에서 대용량 요소 집합을 처리하는 도중 heap 이 상한에 도달했고 V8 Mark-Compact 가 연달아 회수에 실패(Ineffective mark-compacts near heap limit)하여 Runtime_NewArray 시점에 FATAL OOM 으로 abort 되었다. Node 는 SIGABRT 로 종료되었고, 부모 ChildProcessManager::setupEventHandlers 가 stderr pipe 를 logger.error 로 소비하는 구조 때문에 V8 crash dump 의 각 라인이 개별 error 로그가 되어 fingerprint 25개로 파편화되었다.
Technical Analysis#
Code Path#
- Entry point:
applications/agents/packages/cupix-tesla-bim-revision-agent/src/manager/bim-compare.manager.ts:65— parent invokes child via IPC - Child entry:
applications/agents/packages/cupix-tesla-bim-revision-agent/src/process/bim-compare.process.ts:67—ForgeAgent.extract호출 - Failure point: child V8 heap OOM inside forge-agents (
CompareExtractor.compare→GridAlignment.computeGridCorrection; 최근 stderr 에서도 동일 stack 관찰됨) - Log fan-out point:
applications/agents/packages/base/src/manager/child-process.manager.ts:138-141— stderr 데이터 라인 단위로logger.error호출
부모는 fork() 시 execArgv 를 override 하지 않아 child 는 부모의 --max-old-space-size=8192 를 그대로 상속받는다. 실제 로그의 heap upper bound 8231.3 MB 가 이를 확인해 준다.
this.process = fork(actualScriptPath, {
stdio: ['inherit', stdOut, stdErr, 'ipc'],
});
logger.debug(`ChildProcessManager::start | Child process forked with PID: ${this.process.pid}, stdio: ${this.options.stdioMode}`);
child stderr 는 pipe 로 소비되며 각 chunk 가 별도 error 로그가 된다:
this.process.stderr?.on('data', (data: Buffer) => {
const text = data.toString();
logger.error(`ChildProcessManager::setupEventHandlers | Child process stderr: ${text}`);
});
fork 로 spawn 되는 child 가 처리하는 payload 크기:
private async execute(params: BimComparerParams): Promise<ForgeAgent.Result> {
try {
this.log(`BimCompareProcess::execute | forge-agents version: ${forgeAgentsPkg.version}, spec_version: ${SpecVersion}`);
this.log('BimCompareProcess::execute | start');
const result: ForgeAgent.Result = await ForgeAgent.extract(params.apiConfig, params.urn, {
region: params.region,
extractRoom: false,
extractMeta: false,
extractCompare: {
urn: params.previousUrn,
region: params.previousRegion,
query: params.query
}
});
기대 동작 vs 실제 동작: BIM 요소 수(200K+ elements, 150K query entities)가 heap 8 GB 안에서 처리될 것으로 기대되었으나, forge-agents 의 배열 확장 시점에 heap 이 소진되어 V8 이 abort 하였다. 또한 stderr sink 는 crash 시 V8 native stack dump 를 다수의 개별 error 로그로 변환하여 관측 노이즈를 증폭시킨다.
Log Evidence#
Datadog query used:
service:cupixworks-any-bimrevision-agent status:error "ChildProcessManager::setupEventHandlers"
시간 범위 2026-07-21T11:55:00Z ~ 12:05:00Z 결과 26건. 순서대로 핵심 라인:
2026-07-21T12:00:39.174Z Child process stderr: <--- Last few GCs --->
2026-07-21T12:00:39.175Z Child process stderr: [45:0x17bccd20] 814987 ms: Mark-Compact 7909.6 (8231.3) -> 7909.6 (8227.1) MB, 1558.72 / 0.00 ms (average mu = 0.091, current mu = 0.008) allocation failure; scavenge might not succeed
2026-07-21T12:00:39.180Z Child process stderr: [45:0x17bccd20] 816543 ms: Mark-Compact 7916.1 (8233.8) -> 7916.1 (8233.8) MB, 1547.82 / 0.00 ms (average mu = 0.050, current mu = 0.006) allocation failure; scavenge might not succeed
2026-07-21T12:00:39.181Z Child process stderr: <--- JS stacktrace --->
2026-07-21T12:00:39.181Z Child process stderr: FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
2026-07-21T12:00:39.181Z Child process stderr: 1: 0xb78db3 node::OOMErrorHandler
2026-07-21T12:00:39.196Z Child process stderr: 16: 0x12cbb55 v8::internal::ArrayConstructInitializeElements
2026-07-21T12:00:39.196Z Child process stderr: 17: 0x1512628 v8::internal::Runtime_NewArray
2026-07-21T12:01:45.908Z Child process exited
2026-07-21T12:01:45.910Z Process killed by signal SIGABRT (code: null)
2026-07-21T12:01:45.911Z BimRevisionService::run | error: "Process killed by signal SIGABRT (code: null)"
Job 규모 확인:
2026-07-21T11:53:08.911Z BimRevisionService::loadAllElements | end - 211677 cpElements loaded - created: 154331, deleted: 57346
2026-07-21T11:53:11.068Z runBimCompare | params - query.entities.length: 154331, checkExistence.length: 57346, levels: [ROOF, LEVEL 2, LEVEL 1]
최종 결과 (Datadog):
{
"message": "BimRevisionService::REVISE-END",
"bim_id": 17216,
"src_revision_id": 25433,
"prev_revision_id": 21647,
"si_trace_id": "a052780a-c69b-457d-b72d-3aa6332272a3",
"elapsed_ms": 883421,
"status": "error",
"error_message": "Process killed by signal SIGABRT (code: null)",
"counts": {"total": 0, "modified": 0, "removed": 0, "exist": 0}
}
Status board 는 동일 crash 를 svc-scope 인시던트로 인식하고 25 개 클러스터를 묶어 2026-07-21-svc-cupixworks-any-bimrevision-agent--unknown-1 로 이미 resolved 처리하였다 (25개 클러스터 모두 2026-07-21T12:00:39.174Z~12:01:45.911Z 범위, 동일 si_trace_id).
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | forge-agents BIM comparison child process 가 heap 8 GB 상한에 부딪혀 V8 OOM 으로 abort. 대량 elements(154K entities, 57K checkExistence) 처리 중 배열 할당 실패. | Datadog stderr: FATAL ERROR: Ineffective mark-compacts near heap limit, Mark-Compact 두 차례 7909.6 (8231.3) MB → 7916.1 (8233.8) MB 실패, native stack top frame ArrayConstructInitializeElements / Runtime_NewArray; runBimCompare params 로그의 규모(entities=154331, checkExistence=57346, cpElements=211677); 부모 --max-old-space-size=8192 (docker_services/80_run-agent.sh:3) 이 child 로 상속 (fork 시 execArgv override 없음, child-process.manager.ts:74-77). 최종 exit signal SIGABRT (Node OOM abort 시 발생). |
— | Confirmed |
| H2 | Autodesk Forge API_NOT_FOUND (ForgeUtilsError: Resource not found ...AECModelData.json) 이 root cause. |
동일 서비스의 다른 시간대(21:42 KST) 로그에서 API_NOT_FOUND 관찰. |
인시던트 시간(20:53–21:00 KST) 창의 로그에는 API_NOT_FOUND 없음. Failure 유형은 FATAL ERROR: heap out of memory (V8 abort) 이며 Forge HTTP 오류가 아님. si_trace_id: a052780a-c69b-457d-b72d-3aa6332272a3 기반 로그 흐름은 정상 fork → 7.5분 실행 후 OOM. |
Rejected |
| H3 | 부모 ChildProcessManager 가 정상 완료된 child 를 잘못 종료(예: waitForReady timeout, SIGKILL). |
부모에 SIGKILL 을 유발할 timeout 은 execute 옵션 미설정 시 없음(child-process.manager.ts:39-47). stop() 은 정상 종료 경로만 SIGTERM/SIGKILL. |
로그에 부모 발신 kill 흔적 없음. exit signal 이 SIGABRT 로, 이는 V8 이 자체 abort 시 출력. Mark-Compact 실패 native stack 이 child stderr 에 남음 — 외부 kill 이 아닌 self-abort. |
Rejected |
| H4 | 25 개의 개별 fingerprint 는 25 건의 실제 에러이다. | occurrence_count 및 클러스터 수. | 25 클러스터가 모두 2026-07-21T12:00:39.174Z~12:01:45.911Z 창, 동일 si_trace_id, 동일 V8 crash stack. 원인은 부모의 stderr line-level logger.error (child-process.manager.ts:138-141) 로 V8 crash dump 각 라인이 별개 fingerprint 로 분리된 관측 아티팩트. |
Rejected (observability artifact, 실제 사건 1건) |
Fix Recommendation#
즉시 조치 (Critical)#
- 재실행 & 관찰:
bim_id 17216(V4→V5) revision 재시도. 동일 payload 재실행 시 재현 여부 확인. - 별도 코드 변경 없이 즉시 대응 가능한 항목은 재시도뿐. 실 조치는 아래 단기 개선에서 진행.
단기 개선 (1주 이내)#
- child 힙 상한 상향 —
applications/agents/packages/base/src/manager/child-process.manager.ts:74의fork()호출에execArgv: ['--max-old-space-size=<N>']를 명시적으로 지정하고, BIM revision agent 는 컨테이너 메모리 여유 범위 안에서 12–16 GB 로 상향 검토. 부모(8 GB)와 child(8 GB)가 상한을 공유하는 구조는 큰 forge 모델을 감당하기 어렵다. 근거: heap cap8231 MB로그. - 입력 크기 방어 —
applications/agents/packages/cupix-tesla-bim-revision-agent/src/manager/bim-compare.manager.ts:53-79진입 시params.query.entities.length또는checkExistence.length가 임계치를 넘으면 조기 실패(사용자에게 명확한 에러 코드 반환)하거나 chunk 분할 실행. 154K entities 는 이번 실패 규모의 참고선. - stderr 로깅 소음 축소 —
child-process.manager.ts:138-141stderr 파이프를 라인 단위 error 로그로 fan-out 하지 말고, V8 fatal 패턴(FATAL ERROR,<--- JS stacktrace --->, native stack 프레임)은 하나의 error 로그(문맥 포함)로 집약해 방출. observability 시스템(fingerprinter)이 25 개 개별 클러스터로 파편화되지 않도록 함. - 명시적 OOM 사후 진단 로그 — child
close/error핸들러(child-process.manager.ts:107-136)에서signal === 'SIGABRT'이거나 stderr 에FATAL ERROR .* heap out of memory가 관측된 경우, revision 컨텍스트(bim_id, revision id, entities count, checkExistence count,si_trace_id)를 포함한 단일 error 이벤트를 방출.
장기 개선 (재발 방지)#
- compare 파이프라인 스트리밍/청크화 — forge-agents
ForgeAgent.extract의extractCompare.query.entities를 batch 로 분할 처리하고 결과를 병합. 규모가 큰 BIM(수십만 elements)에서는 all-in-memory 방식이 지속 가능하지 않다. - heap dump on OOM — child fork 시
--heapsnapshot-near-heap-limit=1이나--heap-prof를 opt-in 옵션으로 두어 OOM 재발 시 프로파일 확보. - 에이전트 인스턴스 사이징 정책 — BIM revision 에이전트에 한해 큰 인스턴스 유형 사용 또는 revision size 기반 라우팅 도입.
Monitoring#
writing-datadog-monitoring-queries 가이드를 따라 timeseries widget 에 그대로 embed 가능한 count 쿼리 형태로 작성.
- BIM revision agent 의 child OOM abort 발생 카운트:
count:cupixworks_any_bimrevision_agent.child_process.oom{env:production}.as_count()
(위 커스텀 메트릭이 없다면 로그 기반):
logs("service:cupixworks-any-bimrevision-agent status:error \"FATAL ERROR: Ineffective mark-compacts near heap limit\"").index("*").rollup("count").by("environment")
- SIGABRT 로 종료된 child process:
logs("service:cupixworks-any-bimrevision-agent status:error \"Process killed by signal SIGABRT\"").index("*").rollup("count").by("environment")
- BIM revision job 결과별 카운트 (
REVISE-END):
logs("service:cupixworks-any-bimrevision-agent \"REVISE-END\"").index("*").rollup("count").by("@status")
- Query 규모(entities.length) 추이 (facet 화 필요):
logs("service:cupixworks-any-bimrevision-agent \"runBimCompare | params\"").index("*").rollup("count").by("environment")
권장 알람: FATAL ERROR: Ineffective mark-compacts near heap limit OR Process killed by signal SIGABRT 가 1시간 창에서 2회 이상이면 즉시 알림.
Risk Assessment#
- Risk level: medium — 단발 job 실패이나, BIM 규모 성장 추세에서 동일 원인 재발 가능성 상존. 관측성 파편화로 인해 인시던트 감지·분류가 왜곡될 수 있음.
- 예상 복잡도: standard — child heap 상향과 stderr 로깅 집약은 소규모 변경. compare 파이프라인 청크화는 별도 프로젝트 규모.