ES /docs

AnalyzePanoWorker::perform | error on 40003 - The bucket you are attempting to access must be addres

RCA: AnalyzePanoWorker S3 PermanentRedirect on eu-central-1

Overview#

What Happened#

2026-06-24 02:22 KST에 production-EU(eu-central-1) 리전의 cupixworks-worker에서 AnalyzePanoWorker가 capture_id=40003 처리 중 S3 PermanentRedirect로 실패했다. 1회 retry 포함 총 2회 실패 후 capture의 분석 상태가 error_analysis_state로 전이되었다. eu-central-1 worker가 다른 리전에 위치한 cupix-compass-service-production-analysis-model-tkgc 버킷을 자기 리전 엔드포인트로 호출했기 때문이다.

Quick Facts#

Field Value
exception.class Aws::S3::Errors::PermanentRedirect
exception.message The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.
top_frame app/services/cupix/compass/scene_understanding/analyze_pano.rb:96
runtime Ruby on Rails Sidekiq worker (queue analyze_pano, retry: 1)
env production, eu-central-1, tenant cupix

Affected Teams#

Team / Domain Error Count Impact
cupixworks-worker (production-EU) 2 capture_id=40003 의 pano analysis(131 panos) 가 실패하여 error_analysis_state 로 전이. 분석 결과를 사용하는 downstream(compass scene understanding) 미생성.

Timeline#

  1. 2026-04-03 16:21 KST — commit 4f325a541 (TSLA-12266) 가 analyze_model_bucket 을 region별 분기로 변경. apse2 만 명시 분기를 가지고 나머지는 else-tkgc 버킷 사용.
  2. 2026-06-24 02:22:20 KST — eu-central-1 worker가 capture 40003 처리 시작, AnalyzePano::analyze-tkgc 버킷에 put_object 시도 → Aws::S3::Errors::PermanentRedirect. Sidekiq retry.
  3. 2026-06-24 02:22:36 KST — retry 시작 (AnalyzePanoWorker JID-98bb827e0d232c81ac103092).
  4. 2026-06-24 02:22:38 KST — retry도 동일 에러로 실패. retry: 1 소진. capture가 error_analysis_state 로 전이.

Error Log#

Datadog Logs

text
AnalyzePanoWorker::perform | error on 40003 - The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.

Impact#

  • Service: cupixworks-worker
  • 발생 횟수: 2
  • 최초 발생: 2026-06-24 02:22 KST
  • 최근 발생: 2026-06-24 02:22 KST

영향 범위는 production-EU(eu-central-1) 리전에서 실행되는 모든 AnalyzePanoWorker 작업이다. 코드 경로상 동일 리전에서 다른 capture에 대해 작업이 들어오면 동일하게 실패할 가능성이 높다. 다만 현재 클러스터에서 관측된 실패는 capture 40003 한 건뿐이다.

Root Cause Summary#

Cupix::Compass.analyze_model_bucket 는 production 환경에서 Cupix::Tesla.region_codeapse2 일 때만 cupix-compass-service-production-analysis-model-4dom 을 반환하고, 그 외 모든 region(EU 포함)은 else 분기로 cupix-compass-service-production-analysis-model-tkgc 를 반환한다. -tkgc 버킷은 eu-central-1 외부 리전(역사적 default region 인 us-west-2)에 존재한다. 반면 Cupix::Compass::SceneUnderstanding::AnalyzePano#s3_clientAws::S3::Client.new(region: Cupix::Tesla.region) 로 worker가 속한 리전(eu-central-1)에 고정된 client를 생성한다. 따라서 EU worker가 EU 엔드포인트로 us-west-2 버킷에 put_object 를 호출하면 AWS S3 가 HTTP 301 PermanentRedirect 로 응답하고 Aws::S3::Errors::PermanentRedirect 예외가 발생한다. 동일 모듈 내 voxel.rb, thumbnail.rb, siteinsights.rb, cupix_trace.rb 는 모두 euce1 분기를 명시하여 EU 전용 리소스를 사용하는데, compass.rb 만 EU 분기가 누락된 것이 root cause다.

Technical Analysis#

Code Path#

Entry point는 AnalyzePanoWorker#perform 이다.

app/workers/analyze_pano_worker.rb:5-22ruby
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

실제 실패 지점은 AnalyzePano#upload_to_s3s3_client.put_object 호출이다.

app/services/cupix/compass/scene_understanding/analyze_pano.rb:93-108ruby
def upload_to_s3(data_json, capture, index)
  current_time = Time.zone.now
  s3_key = "analyze_pano/#{current_time.year}/#{format('%02d', current_time.month)}/#{format('%02d', current_time.day)}/capture=#{capture.id}_#{index}.json"
  s3_client.put_object(
    bucket: Cupix::Compass.analyze_model_bucket,
    key: s3_key,
    body: data_json.to_json,
    content_type: 'application/json'
  )
  Cupix::Logger.info("AnalyzePanoWorker::upload_to_s3 | upload to s3 on #{s3_key}", class: self.class.name, function: __method__)
  s3_key
end

def s3_client
  @s3_client ||= Aws::S3::Client.new(region: Cupix::Tesla.region)
end

root cause 가 되는 bucket 분기는 다음과 같다.

lib/cupix/compass.rb:4-23ruby
def analyze_model_bucket
  case Cupix::Tesla.tenant
  when 'cupix'
    case Rails.env
    when 'development'
      'cupix-compass-service-development-analyze-model-20js'
    when 'dev'
      'cupix-compass-service-dev-analysis-model-b7vt'
    when 'qa'
      'cupix-compass-service-qa-analysis-model-kgdl'
    when 'production'
      case Cupix::Tesla.region_code
      when 'apse2'
        'cupix-compass-service-production-analysis-model-4dom'
      else
        'cupix-compass-service-production-analysis-model-tkgc'
      end
    end
  end
end

Cupix::Tesla.regionENV['CUPIX_REGION'] || 'us-west-2' 를 반환하므로 EU worker 에서는 eu-central-1 이다. region_code_by_region('eu-central-1')euc1 또는 euce1 을 반환한다(lib/cupix/util/aws.rb:22 의 매핑은 euce1).

lib/cupix/util/aws.rb:18-24ruby
when 'eu-west-3'
  'euwe3'
when 'eu-central-1'
  'euce1'
when 'eu-south-1'

이는 apse2 와 일치하지 않으므로 else 로 떨어져 -tkgc 버킷을 반환한다. 동일 패키지의 다른 모듈은 모두 euce1 분기를 가진다.

lib/cupix/voxel.rb:19-23ruby
case Cupix::Tesla.region_code
when 'euce1'
  'https://yurzr45lb8.execute-api.eu-central-1.amazonaws.com/api/v1/voxels'
when 'apse2'
  'https://54b33rsfwa.execute-api.ap-southeast-2.amazonaws.com/api/v1/voxels'

compass.rbeuce1 분기가 누락되어 EU worker 가 us-west-2 region 버킷을 EU S3 엔드포인트로 호출하게 된다. AWS S3 는 cross-region 요청에 대해 HTTP 301 PermanentRedirect 로 응답하며, aws-sdk-s3 gem 은 이를 Aws::S3::Errors::PermanentRedirect 로 raise 한다.

Log Evidence#

Datadog 쿼리:

text
service:cupixworks-worker @class:AnalyzePanoWorker

핵심 로그 시퀀스(KST):

text
2026-06-24 02:22:20  info   AnalyzePanoWorker::perform | begins on 40003
2026-06-24 02:22:20  info   AnalyzePano::analyze | capture_id=40003 total_panos=131
2026-06-24 02:22:20  error  AnalyzePanoWorker::perform | error on 40003 - The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.
2026-06-24 02:22:13  warn   AnalyzePanoWorker JID-98bb827e0d232c81ac103092: fail: 1.389 sec  (error.msg=Aws::S3::Errors::PermanentRedirect)
2026-06-24 02:22:36  info   AnalyzePanoWorker::perform | begins on 40003
2026-06-24 02:22:38  info   AnalyzePano::analyze | capture_id=40003 total_panos=131
2026-06-24 02:22:38  error  AnalyzePanoWorker::perform | error on 40003 - The bucket you are attempting to access ...
2026-06-24 02:22:37  warn   AnalyzePanoWorker JID-98bb827e0d232c81ac103092: fail: 0.694 sec  (error.msg=Aws::S3::Errors::PermanentRedirect)

error.msg 가 명시적으로 Aws::S3::Errors::PermanentRedirect 임을 보여준다.

json
{
  "status": "warn",
  "message": "AnalyzePanoWorker JID-98bb827e0d232c81ac103092: fail: 0.694 sec",
  "class": "AnalyzePanoWorker",
  "error": { "msg": "Aws::S3::Errors::PermanentRedirect" }
}

AnalyzePano::analyze | capture_id=40003 total_panos=131 로그가 두 번 모두 출력되므로 panos.each_slice(300) 진입 이후의 upload_to_s3 가 실패 지점임이 확인된다 (실패 후 슬라이스 단계의 upload to s3 on ... 로그가 출력되지 않음).

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 compass.rb 의 region 분기 누락으로 EU worker 가 us-west-2 버킷(-tkgc)을 EU S3 endpoint 로 호출 lib/cupix/compass.rb:14-20apse2 만 분기, else-tkgc. s3_clientCupix::Tesla.region(eu-central-1) 고정. AWS는 cross-region put_object 에 대해 PermanentRedirect 반환. 다른 모듈(voxel.rb:19-23, thumbnail.rb:32-34, siteinsights.rb:17-19)은 모두 euce1 분기 보유 Confirmed
H2 capture 40003 의 storage option(capture.storage.s3_source_bucket_name) 이 다른 region 버킷을 가리켜 발생 build_data_json 은 storage 정보를 JSON 본문에 넣을 뿐 S3 API 호출에는 영향 없음 (analyze_pano.rb:34-66). put_object 의 bucket 인자는 Cupix::Compass.analyze_model_bucket 으로 storage 와 무관 코드상 storage 가 사용되는 곳은 JSON payload 빌드뿐임 Rejected
H3 외부 의존성(S3) 자체의 장애 status-board 결과 dep:s3-* 활성 인시던트 없음. AWS Health 알림 없음. 에러 메시지가 일반적 outage 가 아닌 endpoint redirect 안내(301) 정상 동작 시에도 동일 메시지가 나옴 Rejected
H4 Sidekiq retry 로직 자체의 문제 retry: 1 동작 자체는 정상 (실패 → 16초 후 재시도 → 실패) retry 횟수와 무관하게 동일 root cause Rejected

Fix Recommendation#

즉시 조치 (Critical)#

lib/cupix/compass.rb:14-20case Cupix::Tesla.region_code 분기에 euce1 케이스를 추가하여 EU 전용 analysis-model 버킷을 반환하도록 한다. 추가할 버킷 이름과 region 은 인프라팀(또는 cupix-infrastructure의 compass 모듈 정의)에서 확인해야 한다. 동일 모듈의 voxel.rb:19-23, thumbnail.rb:32-34 와 같은 패턴을 따른다. 변경 방향(실제 버킷 이름은 인프라 확인 필요):

lib/cupix/compass.rb:14-20
 when 'production'   case Cupix::Tesla.region_code   when 'apse2'     'cupix-compass-service-production-analysis-model-4dom'+  when 'euce1'+    '<eu-central-1 전용 analysis-model 버킷 이름>'   else     'cupix-compass-service-production-analysis-model-tkgc'   end

배포 전 확인사항:

  • EU 전용 analysis-model 버킷이 실제로 프로비저닝되어 있는지 cupix-infrastructure 에서 확인. 미존재면 인프라 PR 선행.
  • 해당 버킷에 EU worker 의 EB instance role 이 PutObject 권한을 가지는지 IAM 정책 확인 (cupix-infrastructure/cupix-service/api-eb/main.tf:93-94compass_pano_json_s3_bucket ARN wildcard 가 새 버킷을 커버하는지 확인).
  • 버킷이 없다면 임시 mitigation 으로 Aws::S3::Client.new(region: 'us-west-2') 처럼 -tkgc 버킷이 실제 위치한 region 으로 client 를 고정하는 방식도 가능하나, 데이터 locality 와 cross-region 비용 측면에서 권장하지 않는다.

단기 개선 (1주 이내)#

analyze_model_bucketelse fallback 을 제거하고 지원 region 을 명시적 열거로 바꿔 향후 region 추가 시 누락을 컴파일 타임이 아닌 코드 리뷰에서 잡도록 한다. 미지원 region 진입 시 명확한 예외를 raise 하여 cross-region 호출이 silent 하게 발생하는 것을 막는다.

AnalyzePanoWorkerrescue => eAws::S3::Errors::PermanentRedirect 같은 영구 에러를 retry 대상으로 그대로 raise 하는 점도 검토 필요. retry: 1 이긴 하나 명백히 영구적 설정 오류에 대해서는 즉시 dead set 으로 보내는 편이 노이즈를 줄인다.

장기 개선 (재발 방지)#

region 별 리소스 매핑이 4개 이상 파일(compass.rb, voxel.rb, thumbnail.rb, siteinsights.rb, cupix_trace.rb)에 흩어져 있다. region 을 추가할 때마다 모든 파일을 동시에 갱신해야 한다. 단일 region-resource 레지스트리(예: lib/cupix/region_resources.rb)로 통합하면 향후 누락을 구조적으로 방지할 수 있다. 또한 production 부트 시 현재 region 의 모든 필수 외부 리소스(버킷, API endpoint)에 대한 health check(예: HEAD bucket)를 수행하여 cross-region 미스매치를 deploy 직후 감지하도록 한다.

Monitoring#

EU 리전에서 PermanentRedirect 에러 발생 추이:

text
service:cupixworks-worker status:error "PermanentRedirect"

AnalyzePanoWorker 에러 전체 추이(fix 적용 후 0 으로 떨어져야 함):

text
service:cupixworks-worker @class:AnalyzePanoWorker status:error

Sidekiq retry 실패 추이(error.msg 기반):

text
service:cupixworks-worker "Aws::S3::Errors::PermanentRedirect"

Risk Assessment#

  • Risk level: medium. 현재 관측 건수(2회, capture 1건)는 적지만, EU 리전의 모든 AnalyzePanoWorker 실행이 동일 코드 경로를 타므로 EU 트래픽이 증가하면 systemic 실패로 확대된다. capture 의 분석 상태가 error 로 고착되어 사용자 가시 영향이 발생한다.
  • 예상 복잡도: standard. 코드 변경 자체는 1개 파일 1개 분기 추가로 trivial 하지만, 사전에 EU 전용 버킷 프로비저닝과 IAM 정책 확인이 필요하므로 인프라 협업이 동반된다.