SiteTrackPreprocessorRunner::loadAllElementRecords | cpElementRecord-elementRecord match failed - el
RCA: SiteTrackPreprocessorRunner::loadAllElementRecords | cpElementRecord-elementRecord match failed
Error Log#
SiteTrackPreprocessorRunner::loadAllElementRecords | cpElementRecord-elementRecord match failed - elementRecord revisioned_keys: 16756::::2901321#4 (category_id: 1027274, texture_id: none, bim_id: 15567, level_id: 65069) | found cpElementRecord revisioned_key: 17480::::2901321#4 (category_id: 1027274, bim_id: 15567, bim_revision_id: 17480, bim_external_id: 2901321#4)
Impact#
- Service:
cupixworks-sitetrack-preprocessor-agent - Team: accoes
- 발생 횟수: 1206
- 최초 발생: 2026-04-06T20:54:43.628Z
- 최근 발생: 2026-04-07T04:07:21.748Z
Root Cause Summary#
loadAllElementRecords 메서드에서 서버 elementRecord와 로컬 cpElementRecord를 매칭할 때, BIM revision ID 불일치로 인해 키 기반 조회가 실패합니다. cpElementRecord의 identifierKey는 Element API에서 가져온 현재(최신) BIM revision ID로 생성되지만, 서버의 elementRecord.revisioned_keys는 element record가 처음 생성된 시점의 (과거) BIM revision ID를 포함합니다. BIM 모델이 새 revision으로 업데이트되면 이 두 revision ID가 달라져서, 동일한 bim_external_id를 가진 레코드임에도 키 매칭에 실패합니다. 이 에러는 로그만 남기고 처리를 계속하므로 job 자체는 실패하지 않지만, 해당 element record의 서버 데이터(completed_at, task 변경 등)가 누락됩니다.
Technical Analysis#
Code Path#
- Entry point:
preprocessor-service.ts:71—runner.createTargetModels(cpSitetrack)호출 sitetrack-preprocessor-runner.ts:28—createTargetModels내에서loadAllElementRecords호출sitetrack-preprocessor-runner.ts:295-343—loadAllElementRecords메서드 전체
Step 1: 각 cpElement에 대해 makeElementRecords()를 호출하여 cpElementRecord를 생성하고, identifierKey를 맵에 등록합니다.
// cpelement-record.ts:29-30
const textureId = this._cpPhase.srvTextureId || null;
this._identifierKey = `${this._cpElement.cpBim.id}-${this._cpElement.bimRevisionedKey}-${textureId}`;
여기서 bimRevisionedKey는 다음과 같이 구성됩니다:
// cpelement.ts:54
get bimRevisionedKey(): string { return `${this.bimRevisionId}::::${this.bimExternalId}`; }
bimRevisionId는 Element API로부터 받은 현재 BIM revision의 ID입니다.
Step 2: 서버에서 getElementRecords API를 호출하여 srvElementRecords를 가져옵니다.
// sitetrack-preprocessor-runner.ts:310
const srvElementRecords = await this.cupixApi.siteinsights.getElementRecords(cpSitetrack.facilityKey, chunk, uniqueLevelIds, cpSitetrack.recordCapturedAt);
Step 3 (Failure point): 서버 elementRecord의 revisioned_keys를 사용하여 맵에서 cpElementRecord를 찾습니다.
// sitetrack-preprocessor-runner.ts:319-325
for (const revisionKey of elementRecord.revisioned_keys) {
const key = `${elementRecord.bim?.id}-${revisionKey}-${textureId}`;
if (cpSitetrack.cpElementRecordMap.has(key)) {
cpElementRecord = cpSitetrack.cpElementRecordMap.get(key);
break;
}
}
기대 동작: revisionKey(예: 16756::::2901321#4)가 맵의 키(예: 15567-16756::::2901321#4-null)와 일치해야 합니다.
실제 동작: 맵의 키는 현재 revision ID를 사용하여 15567-17480::::2901321#4-null로 생성되었으므로, 서버의 과거 revision ID 16756과 일치하지 않습니다.
Step 4: 매칭 실패 시 logElementRecordMatchFailure가 호출됩니다.
// element-record.util.ts:22-45
if (isTargetBim) {
// bimExternalId로 fallback 매칭을 시도하여 디버그 정보 출력
const foundRecord = elementRecord.revisioned_keys?.reduce<CPElementRecord | undefined>((found, revisionedKey) => {
if (found) return found;
const bimExternalId = revisionedKey.split('::::')[1] || revisionedKey;
return cpElementRecordArray.find(cp =>
cp.bimExternalId === bimExternalId &&
cp.cpElement.cpBim.id === elementRecord.bim?.id
);
}, undefined);
// ... logger.error with both revisioned_keys for comparison
}
이 유틸 함수에서 bimExternalId 기반으로 relaxed matching을 수행하면 레코드를 찾을 수 있음을 확인합니다 — 이는 bim_external_id는 동일하지만 bim_revision_id만 다르다는 것을 증명합니다.
Log Evidence#
사용한 Datadog 쿼리:
service:cupixworks-sitetrack-preprocessor-agent status:error "loadAllElementRecords"
패턴 1 — bim_id: 15567, revision 16756 vs 17480:
SiteTrackPreprocessorRunner::loadAllElementRecords | cpElementRecord-elementRecord match failed - elementRecord revisioned_keys: 16756::::2901321#4 (category_id: 1027274, texture_id: none, bim_id: 15567, level_id: 65069) | found cpElementRecord revisioned_key: 17480::::2901321#4 (category_id: 1027274, bim_id: 15567, bim_revision_id: 17480, bim_external_id: 2901321#4)
패턴 2 — bim_id: 14742, revision 16763 vs 21770 (다른 facility):
SiteTrackPreprocessorRunner::loadAllElementRecords | cpElementRecord-elementRecord match failed - elementRecord revisioned_keys: 16763::::76901e65-45f6-495b-b572-3c5a9ab425b4-0020fc46 (category_id: 1030905, texture_id: none, bim_id: 14742, level_id: 62492) | found cpElementRecord revisioned_key: 21770::::76901e65-45f6-495b-b572-3c5a9ab425b4-0020fc46 (category_id: 1030905, bim_id: 14742, bim_revision_id: 21770, bim_external_id: 76901e65-45f6-495b-b572-3c5a9ab425b4-0020fc46)
두 패턴 모두 동일한 근본 원인을 보여줍니다:
bim_external_id는 동일 (예:2901321#4,76901e65-...-0020fc46)bim_id는 동일 (예: 15567, 14742)bim_revision_id만 다름 (예: 16756→17480, 16763→21770 — 서버 ElementRecord가 과거 revision 참조)
서비스 실행 컨텍스트:
service:cupixworks-sitetrack-preprocessor-agent status:info "PreprocessorService::run"
2026-04-07 13:04:47 KST - PreprocessorService::run | begin
2026-04-07 13:10:13 KST - PreprocessorService::run | end
2026-04-07 13:38:41 KST - PreprocessorService::run | begin
2026-04-07 13:39:45 KST - PreprocessorService::run | end
여러 preprocessor job이 지속적으로 실행되며, 각 실행마다 동일한 매칭 실패가 반복됩니다. Job 자체는 성공적으로 완료(end 로그 존재)되지만, 매칭 실패한 element record들은 서버 데이터 없이 처리됩니다.
관련 에러 (같은 시간대, 다른 에러):
service:cupixworks-sitetrack-preprocessor-agent status:error -"loadAllElementRecords"
CPElement::makeElementRecords | No phases found for Element bim_external_id: 9bb53fe0-15d5-43bd-99d2-690240742c4d-007a6f36, category_id: 1030869, workarea_ids: [ 103620 ]
이 에러는 관련되어 있지만 별도의 이슈입니다 — phase가 없는 element에 대한 것으로, 이 RCA의 대상과는 다릅니다.
Fix Recommendation#
즉시 조치 (Critical)#
- 파일:
sitetrack-preprocessor-runner.ts:319-325 - 방향: 키 매칭 시
bim_revision_id를 제외한 조회를 fallback으로 추가. 현재logElementRecordMatchFailure유틸에서 이미bimExternalId+bim.id로 relaxed matching을 수행하여 레코드를 찾고 있으므로, 이 로직을 실제 매칭에도 적용. - 구체적으로:
cpElementRecordMap의 full key 매칭이 실패하면,bim.id+bimExternalId+textureId조합으로 2차 조회를 시도.element-record.util.ts:24-31의 기존 fallback 로직을 참고.
단기 개선 (1주 이내)#
CPElementRecord의identifierKey생성 로직 재설계 검토.bim_revision_id를 키의 일부로 포함할 필요성 재평가 — BIM 모델 업데이트 시 element record의 revision이 자연스럽게 바뀌는 것이 예상 동작이라면, 키에서 revision을 제거하는 것이 근본적 해결.- 또는 서버의
getElementRecordsAPI가 현재 revision의revisioned_keys도 함께 반환하도록 API 변경을 요청.
장기 개선 (재발 방지)#
- Element record 매칭 로직에 대한 통합 테스트 추가 — BIM revision이 업데이트된 시나리오를 포함.
logElementRecordMatchFailure의 fallback matching 결과를 메트릭으로 수집하여, revision mismatch가 발생하는 facility/bim 조합을 모니터링.
Monitoring#
- 추가할 메트릭:
cpElementRecord-elementRecord match failed에러 발생 횟수를 facility_key, bim_id별로 집계 - Datadog 쿼리:
service:cupixworks-sitetrack-preprocessor-agent status:error "cpElementRecord-elementRecord match failed"
- 현재 1206건/7시간 수준으로 발생 중이며, BIM revision 업데이트가 있는 facility에서 반복적으로 발생. Fix 적용 후 이 에러 수가 0에 근접해야 함.
Risk Assessment#
- Risk level: medium
- 예상 복잡도: standard — 매칭 로직 변경은 단순하나, revision을 키에서 제거할 경우 다른 곳에서 revision 구분이 필요한 케이스가 없는지 확인 필요.
logElementRecordMatchFailure에서 이미 fallback matching을 구현하고 있으므로 방향은 검증됨.