ES /docs

AnalysisTimeoutWorker::perform | timeout on 686589

RCA: AnalysisTimeoutWorker::perform | timeout on 686589

Overview#

What Happened#

2026-04-24 18:05:00 KST에 cupixworks-worker 서비스의 AnalysisTimeoutWorker가 capture 686589에 대해 3시간 timeout을 감지하고 error 상태로 전환했다. 이는 AnalyzePanoWorker가 15:04:58 KST에 pano vectorize 분석을 시작한 후, 외부 분석 서비스(compass)로부터 3시간 이내에 analysis_done callback을 수신하지 못했음을 의미한다. 14일간 동일 패턴의 timeout이 19건 발생하여 반복적 문제로 확인된다.

Quick Facts#

Field Value
exception.class AnalysisTimeoutWorker
exception.message AnalysisTimeoutWorker::perform | timeout on 686589
top_frame app/workers/analysis_timeout_worker.rb:10
env production, us-west-2

Timeline#

  1. 2026-04-24 16:09:30 KST — Capture 686589 관련 sitetrack/deviation 업데이트 및 CPC 로그 수집 (job 1034245)
  2. 2026-04-24 15:04:58 KSTAnalyzePanoWorker 시작, S3에 분석 데이터 업로드 완료, 3시간 timeout worker 스케줄됨
  3. 2026-04-24 18:04:59 KSTAnalysisTimeoutWorker JID-4e790220 실행 (0.291초 소요)
  4. 2026-04-24 18:05:00 KST — Capture 686589이 여전히 processing 상태 → timeout 에러 로그 발생, error 상태로 전환

Error Log#

Datadog Logs

text
AnalysisTimeoutWorker::perform | timeout on 686589

Impact#

  • Service: cupixworks-worker
  • 발생 횟수: 1 (이 클러스터), 19건 (최근 14일 동일 패턴)
  • 최초 발생: 2026-04-24T09:05:00.605Z
  • 최근 발생: 2026-04-24T09:05:00.605Z

Root Cause Summary#

AnalyzePanoWorker가 capture 686589의 pano 데이터를 S3에 업로드하고 외부 compass 분석 서비스의 callback을 기다리는 구조에서, 외부 서비스가 3시간 timeout 내에 analysis_done callback을 보내지 않았다. AnalysisTimeoutWorker는 설계대로 3시간 후 capture의 analysis_state가 여전히 processing임을 확인하고 error 상태로 전환했다. 근본 원인은 downstream compass 분석 서비스 측에서 capture 686589의 분석을 완료하지 못했거나 callback 발송에 실패한 것이다. 이 패턴은 최근 14일간 19건 발생하여 외부 분석 파이프라인의 안정성 문제가 반복되고 있다.

Technical Analysis#

Code Path#

  • Entry point: app/workers/analyze_pano_worker.rb:5AnalyzePanoWorker#perform(capture_id)
app/workers/analyze_pano_worker.rb:5-21ruby
def perform(capture_id)
  capture = ::Capture.find(capture_id)
  Cupix::Logger.info("AnalyzePanoWorker::perform | begins on #{capture_id}", class: self.class.name, function: __method__)

  return unless capture.analyzable?

  begin
    capture.processing_analysis_state!
    Cupix::Compass::SceneUnderstanding::AnalyzePano.analyze(capture)
    AnalysisTimeoutWorker.perform_in(3.hours, capture_id)

    Cupix::Logger.info("AnalyzePanoWorker::perform | uploaded and waiting callback on #{capture_id}", class: self.class.name, function: __method__)
  rescue => e
    capture.error_analysis_state!
    Cupix::Logger.error("AnalyzePanoWorker::perform | error on #{capture_id} - #{e.message}", class: self.class.name, function: __method__)
    raise e
  end
end
  1. AnalyzePanoWorker가 capture의 analysis_stateprocessing으로 전환
  2. AnalyzePano.analyze가 pano 데이터를 JSON으로 구성하여 S3에 업로드 (callback URL 포함)
  3. AnalysisTimeoutWorker.perform_in(3.hours, capture_id)로 3시간 후 timeout 체크 스케줄
  • S3 upload + callback 구성: app/services/cupix/compass/scene_understanding/analyze_pano.rb:68-73
app/services/cupix/compass/scene_understanding/analyze_pano.rb:68-73ruby
def build_callback(capture)
  token = OpenSSL::HMAC.hexdigest('SHA256', Rails.application.secret_key_base, "analysis_done:#{capture.id}")
  {
    url: "#{$API_URL}/api/v1/captures/#{capture.id}/callbacks/analysis_done",
    token: token
  }
end

S3에 업로드된 JSON에 callback URL과 HMAC token이 포함된다. 외부 compass 분석 서비스가 처리를 완료하면 이 callback을 호출해야 한다.

  • Callback handler: app/controllers/api/v1/captures_controller.rb:128-133
app/controllers/api/v1/captures_controller.rb:128-133ruby
unless capture.analysis_state_processing?
  raise Cupix::Errors::InvalidState.new(code: 'ARG10001', reason: "Invalid analysis_state: #{capture.analysis_state}")
end

capture.done_analysis_state!
Cupix::Logger.info("CapturesController::analysis_done_callback | done on #{capture.id}", class: self.class.name, function: __method__)

정상 흐름이면 외부 서비스가 callback을 호출하여 analysis_statedone으로 전환한다.

  • Failure point: app/workers/analysis_timeout_worker.rb:5-11
app/workers/analysis_timeout_worker.rb:5-11ruby
def perform(capture_id)
  capture = ::Capture.find_by(id: capture_id)
  return unless capture&.analysis_state_processing?

  capture.error_analysis_state!
  Cupix::Logger.error("AnalysisTimeoutWorker::perform | timeout on #{capture_id}", class: self.class.name, function: __method__)
end

3시간 후 AnalysisTimeoutWorker가 실행될 때 capture의 analysis_state가 여전히 processing이면 timeout으로 판단하고 error로 전환한다. Capture 686589는 callback을 수신하지 못하여 processing 상태가 유지되었고, timeout이 발동했다.

Log Evidence#

분석 시작 시점 확인:

text
Datadog query: service:cupixworks-worker "AnalyzePanoWorker" 686589
Time range: 2026-04-24T04:00:00Z ~ 2026-04-24T10:00:00Z
json
{"timestamp": "2026-04-24 15:04:58 KST", "status": "info", "message": "AnalyzePanoWorker::perform | begins on 686589"}
{"timestamp": "2026-04-24 15:04:58 KST", "status": "info", "message": "AnalyzePanoWorker::perform | uploaded and waiting callback on 686589"}

S3 업로드 성공 후 callback 대기 상태 진입 확인.

Callback 미수신 확인:

text
Datadog query: service:cupixworks-api "analysis_done" 686589
Time range: 2026-04-24T06:00:00Z ~ 2026-04-24T10:00:00Z
Result: 0 logs

Capture 686589에 대한 analysis_done callback은 수신되지 않았다. 동일 시간대에 다른 capture(69490, 69537, 686628 등)들은 정상적으로 callback을 수신한 것으로 확인된다.

Timeout 에러 발생:

text
Datadog query: service:cupixworks-worker 686589
Time range: 2026-04-24T07:00:00Z ~ 2026-04-24T10:00:00Z
json
{"timestamp": "2026-04-24 18:05:00 KST", "status": "error", "message": "AnalysisTimeoutWorker::perform | timeout on 686589", "class": "AnalysisTimeoutWorker", "function": "perform"}

분석 시작(15:04:58)부터 timeout(18:05:00)까지 정확히 3시간 경과. perform_in(3.hours) 스케줄과 일치.

최근 14일간 동일 패턴 빈도:

text
Datadog query: service:cupixworks-worker status:error "AnalysisTimeoutWorker"
Time range: 2026-04-10T00:00:00Z ~ 2026-04-24T10:00:00Z
Result: 19건

4/11(1건), 4/14(3건), 4/15(1건), 4/16(1건), 4/17(2건), 4/21(4건), 4/22(5건), 4/24(1건)으로 점진적 증가 추세.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 Downstream compass 분석 서비스가 capture 686589의 분석을 완료하지 못하거나 callback 발송 실패 callback 미수신 확인 (Datadog query: 0 logs), 동일 시간대 다른 capture는 callback 정상 수신, 14일간 19건 반복 발생 Confirmed
H2 AnalyzePano.analyze의 S3 업로드 실패로 분석이 시작되지 않음 AnalyzePanoWorker가 "uploaded and waiting callback" 로그를 정상 출력 (analyze_pano_worker.rb:16), S3 업로드 성공 후 timeout worker가 스케줄됨 Rejected
H3 Callback URL 또는 token 오류로 외부 서비스가 callback을 보냈으나 인증 실패 API 로그에 callback 시도 자체가 없음 (0 logs), 인증 실패 시 error 로그가 기록되어야 하나 없음 (captures_controller.rb:124) Rejected
H4 3시간 timeout이 너무 짧아서 대용량 capture 분석이 완료되기 전에 timeout 발동 14일간 19건 반복 발생하여 일부 capture에서는 3시간이 부족할 수 있음 동일 시간대 다른 capture는 3시간 이내 정상 완료, 대부분의 capture는 timeout 없이 완료됨 Inconclusive

Fix Recommendation#

즉시 조치 (Critical)#

  • 이 에러는 AnalysisTimeoutWorker설계된 동작이다. Timeout guard가 정상 작동하여 processing 상태에 영구적으로 머무르는 capture를 error로 전환하고 있다.
  • 근본 원인은 downstream compass 분석 서비스에 있으므로, 해당 서비스의 로그를 확인하여 capture 686589 분석 실패 원인을 파악해야 한다.
  • compass 분석 서비스의 S3 이벤트 트리거, Lambda/container 실행 상태, callback 발송 로직을 점검해야 한다.

단기 개선 (1주 이내)#

  • AnalysisTimeoutWorker의 에러 로그에 추가 컨텍스트를 포함하여 디버깅을 용이하게 해야 한다. 현재는 capture_id만 로그에 남기므로, analysis_state_updated_at (processing 전환 시각), pano 수 등의 정보를 포함하면 timeout 원인 분석이 쉬워진다.
  • error 레벨 대신 warn 레벨로 변경을 검토할 수 있다. 이 timeout은 외부 서비스 의존성에 의한 예상된 실패 시나리오이므로, 코드 버그가 아닌 운영 이슈에 해당한다. 단, 발생 빈도가 증가하고 있으므로 현재는 error 레벨을 유지하되 빈도 모니터링이 필요하다.

장기 개선 (재발 방지)#

  • Compass 분석 서비스에 health check 또는 heartbeat 메커니즘을 도입하여, 분석 시작 여부와 진행 상태를 확인할 수 있게 해야 한다.
  • Timeout 발생 시 자동 재시도 메커니즘 도입을 검토할 수 있다. 현재는 error 상태로 전환만 하고 재시도하지 않는데, run_analysis?analysis_statecreated 또는 error일 때만 analyzable?을 true로 반환하므로 (analyzable.rb:60), 수동으로 상태를 재설정하거나 자동 재시도 로직을 추가할 수 있다.
  • S3 업로드 후 downstream 서비스의 수신 확인(acknowledgment) 단계를 추가하여, 분석 요청이 실제로 처리 큐에 들어갔는지 확인하는 방안을 고려할 수 있다.

Monitoring#

  • Timeout 빈도 추적:
text
service:cupixworks-worker status:error "AnalysisTimeoutWorker::perform | timeout"
  • 주간 발생 건수 추이를 모니터링하여 증가 시 알림 설정 (현재 14일간 19건, 주당 약 9-10건)
  • Callback 성공률 모니터링:
text
service:cupixworks-api "callbacks/analysis_done"
  • 분석 시작 대비 callback 수신 비율을 추적하여 외부 서비스 안정성을 측정

Risk Assessment#

  • Risk level: medium
  • 예상 복잡도: standard — tesla 측 코드는 정상 동작하고 있으며, 근본 원인은 downstream compass 분석 서비스에 있다. Tesla 측에서는 로그 개선과 재시도 메커니즘 추가가 주요 작업이다.