RefinementService::loadCluster | end - not found selected_unrefined_cluster_id
RCA: RefinementService::loadCluster | end - not found selected_unrefined_cluster_id
Overview#
What Happened#
2026-04-20 08:28:46 UTC, cupixworks-capture-refinement-arm-instance 서비스에서 capture 69108에 대한 refinement 작업이 실패했다. Refinement agent가 시작되어 job 179056을 로드한 후, capture의 selected_unrefined_cluster_id 필드가 undefined여서 cluster를 로드하지 못하고 에러로 종료되었다. ap-southeast-2 리전의 tgsp-jv 팀 사용자에게 영향이 있었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.message | RefinementService::loadCluster | end - not found selected_unrefined_cluster_id |
| top_frame | refinement-service.ts:179 |
| runtime | Node.js (filebeat 7.17.15) |
| env | production, ap-southeast-2 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| tgsp-jv | 1 | Capture 69108의 refinement 처리 실패, refinement_state가 error로 전환됨 |
Timeline#
- 08:27:49 UTC — Capture 69108 업데이트, refinement_state
draft → ready전환 - 08:28:04 UTC —
start_finalization→create_editing_entity호출 - 08:28:27 UTC — Capture bulk_operation 실행 (capture 69110 → 69108)
- 08:28:29 UTC — Refinement_state
ready → queued, refinement job 179056 생성 - 08:28:46 UTC — Refinement agent 시작: init → authenticate → run → loadJob
- 08:28:46 UTC —
loadCluster에서selected_unrefined_cluster_id누락 에러 발생 - 08:28:46 UTC —
RefinementService::run | end - {}에러로 catch됨 - 08:28:47 UTC —
terminateService10초 후 강제 종료 예약 - 08:28:49 UTC — Refinement_state
queued → error전환, postprocessor agent 실행
Error Log#
RefinementService::loadCluster | end - not found selected_unrefined_cluster_id
Impact#
- Service:
cupixworks-capture-refinement-arm-instance - Team: tgsp-jv
- 발생 횟수: 1
- 최초 발생: 2026-04-20T08:28:46.826Z
- 최근 발생: 2026-04-20T08:28:46.826Z
Root Cause Summary#
Capture 69108의 selected_unrefined_cluster_id 필드가 설정되지 않은 상태에서 refinement job이 생성되어 실행되었다. Tesla(Rails) 측의 refinement state machine이 ready → queued 전환 시 run_capture_refinement를 호출하여 job을 생성하지만, selected_unrefined_cluster_id가 설정되어 있는지 사전 검증하지 않는다. 이 필드는 API를 통해 외부에서(UI 또는 editing entity flow) 설정되어야 하는데, 이번 케이스에서는 설정되지 않은 채로 refinement가 트리거되었다. Refinement agent의 loadCluster 메서드에서 cpCapture.srvCapture?.selected_unrefined_cluster_id가 undefined로 평가되어 즉시 에러를 throw했다.
Technical Analysis#
Code Path#
1. Refinement state machine transition (Tesla)
Capture의 refinement_state가 ready → queued로 전환되면 run_capture_refinement가 호출된다.
before_transition any => :queued do |model, transition|
model.run_capture_refinement(model)
end
def run_capture_refinement(model)
capture_invoker = CaptureInvoker.new(model: model, current_user: model.user, current_team: model.team)
capture_invoker.create_refinement
end
2. Job creation validation (Tesla) — 검증 누락 지점
create_refinement은 create_capture_refinement_invokable?을 호출하지만, 이 validation은 중복 job 존재 여부만 확인한다. selected_unrefined_cluster_id 설정 여부는 검증하지 않는다.
def create_capture_refinement_invokable?
if self.jobs.processing.where(kind: 'create_capture_refinement').exists?
raise Cupix::Errors::Entity.new(code: 'ENT10000', reason: 'Capture has processing create_capture_refinement job')
end
end
def create_refinement(opts = {})
@model.create_capture_refinement_invokable?
params = {
jobable: @model,
user: self.current_user,
team: self.current_team
}
# ... opts 처리 ...
job = CreateCaptureRefinementJob.create!(params)
# selected_unrefined_cluster_id 검증 없이 job 생성
end
3. Refinement agent loadCluster — 실패 지점
Agent가 capture API에서 capture 데이터를 가져온 후, selected_unrefined_cluster_id를 읽으려 하지만 값이 undefined이다.
private loadCluster = async (cpCapture: CPCapture): Promise<void> => {
logger.debug('RefinementService::loadCluster | begin');
const clusterId = cpCapture.srvCapture?.selected_unrefined_cluster_id;
if (clusterId == undefined) {
logger.error('RefinementService::loadCluster | end - not found selected_unrefined_cluster_id');
this.jobManager.setErrorCode(ErrorCode.ScenemapperUtils.RefinerNotFoundCluster);
throw new Error('not found selected_unrefined_cluster_id');
}
// ...
};
srvCapture는 TESLA.Capture API 응답 객체이며, selected_unrefined_cluster_id 타입은 number | undefined이다.
get srvCapture(): TESLA.Capture | undefined { return this._serverModel; }
4. selected_unrefined_cluster_id 설정 경로 (Tesla)
이 필드는 capture의 sys JSON 컬럼에 저장되며, update_selected_unrefined_cluster_id 메서드를 통해서만 설정된다.
def update_selected_unrefined_cluster_id(params)
return if params[:selected_unrefined_cluster_id].blank?
_cluster = ::Cluster.find(params[:selected_unrefined_cluster_id])
if _cluster.present? && _cluster.cluster_type != 'unrefined'
raise Cupix::Errors::Parameter.new(code: 'ARG10001', reason: 'selected_unrefined_cluster_id must be unrefined cluster')
end
@model.selected_unrefined_cluster_id = params[:selected_unrefined_cluster_id]
end
이 메서드는 capture update API 호출 시 파라미터로 전달될 때만 실행된다. 자동으로 설정되는 경로가 없다.
5. reset_refinement에서 nil로 초기화
def reset_refinement
@model.selected_unrefined_cluster_id = nil
# ...
end
Log Evidence#
Datadog에서 capture 69108의 전체 실행 흐름을 추적한 결과:
service:cupixworks-capture-refinement-arm-instance @capture.id:69108
08:28:46.506Z [info] RefinementService::init
08:28:46.507Z [info] RefinementService::authenticate | begin
08:28:46.554Z [info] CupixAuth::setSession | session_id: 691ccee0bf047b658f2a888cf7db01dae6e38b18
08:28:46.554Z [info] RefinementService::authenticate | end
08:28:46.555Z [info] RefinementService::run | begin
08:28:46.555Z [info] JobManager::loadJob | begin - job id: 179056
08:28:46.595Z [info] JobManager::loadJob | end - job id: 179056
08:28:46.826Z [error] RefinementService::loadCluster | end - not found selected_unrefined_cluster_id
08:28:46.826Z [error] RefinementService::run | end - {}
08:28:47.625Z [info] RefinementService::terminateService | force shutdown after 10 seconds
Agent가 init → authenticate → run → loadJob까지 정상 실행 후, loadCluster에서 즉시 실패했다. loadJob에서 run까지 0.23초 만에 에러가 발생하여, API에서 capture 데이터를 가져온 직후 selected_unrefined_cluster_id가 없음을 확인한 것이다.
Tesla API 측 로그:
service:cupixworks-api 69108
08:27:50Z [info] refinement_state has transitioned from draft to ready on Capture 69108
08:28:04Z [info] create editing entity on start finalization Capture 69108
08:28:04Z [info] create editing entity on Capture 69108
08:28:29Z [info] Refinement job is created for capture 69108. job_id: 179056
08:28:29Z [info] refinement_state has transitioned from ready to queued on Capture 69108
08:28:49Z [info] refinement_state has transitioned from queued to error on Capture 69108
selected_unrefined_cluster_id 설정에 대한 로그가 전혀 없다. Refinement state가 draft → ready → queued로 자동 전환되면서 job이 생성되었지만, cluster ID는 외부에서 설정되지 않았다.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | selected_unrefined_cluster_id가 설정되지 않은 상태에서 refinement job이 생성됨 — Tesla 측 validation 부재 |
create_capture_refinement_invokable?가 중복 job만 검증 (invokable/capture.rb:67-71). API 로그에 selected_unrefined_cluster 설정 관련 기록 없음. Agent 로그에서 loadCluster 즉시 실패 |
— | Confirmed |
| H2 | API 응답에서 selected_unrefined_cluster_id 필드가 누락됨 (serialization 문제) |
— | capture_serializer.rb:51에서 해당 필드를 attribute로 포함하고 있음. 필드가 nil이면 API 응답에서 undefined/null로 반환되는 것이 정상 동작 |
Rejected |
| H3 | reset_refinement이 호출되어 이미 설정된 cluster ID가 초기화됨 |
reset_refinement에서 selected_unrefined_cluster_id = nil 설정 (capture_invoker.rb:315) |
API 로그에서 reset_refinement 호출 기록 없음. Refinement state가 draft → ready → queued로 순차 전환되어 reset이 개입하지 않음 |
Rejected |
| H4 | Editing entity done callback에서 cluster ID 설정 없이 queued 전환 트리거 |
done_state_callback_editing_entity는 queued_refinement_state!만 호출하고 cluster ID를 설정하지 않음 (editing_entity.rb:190-196). 로그에서 editing entity 생성 후 자동으로 queued 전환 확인 |
— | Confirmed |
Fix Recommendation#
즉시 조치 (Critical)#
app/models/concerns/invokable/capture.rb:67-71의create_capture_refinement_invokable?메서드에selected_unrefined_cluster_id존재 여부 검증을 추가해야 한다.- 해당 필드가 nil이면 refinement job 생성을 차단하여, agent가 불필요하게 실행되는 것을 방지한다.
단기 개선 (1주 이내)#
app/models/concerns/refinementable.rb:55-57의before_transition any => :queued훅에서selected_unrefined_cluster_id가 설정되어 있는지 확인하는 guard를 추가해야 한다. 설정되지 않은 경우 transition을 거부하거나 에러 상태로 전환한다.app/models/concerns/finalization/editing_entity.rb:190-196의done_state_callback_editing_entity에서queued_refinement_state!호출 전에selected_unrefined_cluster_id가 설정되어 있는지 확인해야 한다.
장기 개선 (재발 방지)#
- Refinement 워크플로우에서
selected_unrefined_cluster_id자동 설정 메커니즘을 검토할 필요가 있다. 현재는 외부 API 호출에 의존하므로, editing entity flow에서 자동으로 적절한 unrefined cluster를 선택하는 로직을 고려한다. - Capture model에 refinement 관련 필수 필드에 대한 custom validation을 추가하여, state transition 시 데이터 정합성을 보장한다.
Monitoring#
selected_unrefined_cluster_id누락으로 인한 실패를 추적하는 메트릭 추가- Datadog 쿼리 예시:
service:cupixworks-capture-refinement-arm-instance "not found selected_unrefined_cluster_id" status:error
- Refinement job 생성 시
selected_unrefined_cluster_idnil 비율 모니터링:
service:cupixworks-api "Refinement job is created" -"selected_unrefined_cluster"
Risk Assessment#
- Risk level: medium
- 예상 복잡도: standard — Tesla 측 validation 추가가 주요 변경점이며, refinement agent 코드 변경은 불필요하다.