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#
- 2026-04-03 16:21 KST — commit
4f325a541(TSLA-12266) 가analyze_model_bucket을 region별 분기로 변경.apse2만 명시 분기를 가지고 나머지는else로-tkgc버킷 사용. - 2026-06-24 02:22:20 KST — eu-central-1 worker가 capture 40003 처리 시작,
AnalyzePano::analyze가-tkgc버킷에 put_object 시도 →Aws::S3::Errors::PermanentRedirect. Sidekiq retry. - 2026-06-24 02:22:36 KST — retry 시작 (
AnalyzePanoWorker JID-98bb827e0d232c81ac103092). - 2026-06-24 02:22:38 KST — retry도 동일 에러로 실패.
retry: 1소진. capture가error_analysis_state로 전이.
Error Log#
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_code 가 apse2 일 때만 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_client 는 Aws::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 이다.
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_s3 의 s3_client.put_object 호출이다.
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 분기는 다음과 같다.
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.region 은 ENV['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).
when 'eu-west-3'
'euwe3'
when 'eu-central-1'
'euce1'
when 'eu-south-1'
이는 apse2 와 일치하지 않으므로 else 로 떨어져 -tkgc 버킷을 반환한다. 동일 패키지의 다른 모듈은 모두 euce1 분기를 가진다.
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.rb 만 euce1 분기가 누락되어 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 쿼리:
service:cupixworks-worker @class:AnalyzePanoWorker
핵심 로그 시퀀스(KST):
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 임을 보여준다.
{
"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-20 에 apse2 만 분기, else는 -tkgc. s3_client 는 Cupix::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-20 의 case Cupix::Tesla.region_code 분기에 euce1 케이스를 추가하여 EU 전용 analysis-model 버킷을 반환하도록 한다. 추가할 버킷 이름과 region 은 인프라팀(또는 cupix-infrastructure의 compass 모듈 정의)에서 확인해야 한다. 동일 모듈의 voxel.rb:19-23, thumbnail.rb:32-34 와 같은 패턴을 따른다. 변경 방향(실제 버킷 이름은 인프라 확인 필요):
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-94의compass_pano_json_s3_bucketARN wildcard 가 새 버킷을 커버하는지 확인). - 버킷이 없다면 임시 mitigation 으로
Aws::S3::Client.new(region: 'us-west-2')처럼-tkgc버킷이 실제 위치한 region 으로 client 를 고정하는 방식도 가능하나, 데이터 locality 와 cross-region 비용 측면에서 권장하지 않는다.
단기 개선 (1주 이내)#
analyze_model_bucket 의 else fallback 을 제거하고 지원 region 을 명시적 열거로 바꿔 향후 region 추가 시 누락을 컴파일 타임이 아닌 코드 리뷰에서 잡도록 한다. 미지원 region 진입 시 명확한 예외를 raise 하여 cross-region 호출이 silent 하게 발생하는 것을 막는다.
AnalyzePanoWorker 의 rescue => e 가 Aws::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 에러 발생 추이:
service:cupixworks-worker status:error "PermanentRedirect"
AnalyzePanoWorker 에러 전체 추이(fix 적용 후 0 으로 떨어져야 함):
service:cupixworks-worker @class:AnalyzePanoWorker status:error
Sidekiq retry 실패 추이(error.msg 기반):
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 정책 확인이 필요하므로 인프라 협업이 동반된다.