ES /docs

S3 HEAD latency spike unprotected by timeout/retry

RCA: Api::V1::PointcloudsController#check_octree_uploading Latency (1064ms)

Overview#

What Happened#

2026-05-26 23:39 UTC에 cupixworks-api 서비스의 check_octree_uploading 엔드포인트에서 1064ms의 응답 지연이 발생했다. 이 엔드포인트는 octree 파일 업로드 완료 여부를 확인하기 위해 AWS S3 HEAD 요청을 동기적으로 수행하며, S3 응답 지연이 전체 API 응답 시간에 직접 반영되는 구조이다.

Quick Facts#

Field Value
resource_name Api::V1::PointcloudsController#check_octree_uploading
top_frame app/models/concerns/octree.rb:9
env production, us-west-2
avg_duration 1064ms
cluster_type latency

Timeline#

  1. 2026-05-26T23:39:23Z — check_octree_uploading 요청에서 1064ms 지연 감지 (APM trace ID: 3703117997578681932)
  2. 2026-05-27 — Error sweeper 자동 수집 및 RCA 분석

Error Log#

Datadog Logs

text
{
  "resource_name": "Api::V1::PointcloudsController#check_octree_uploading",
  "service": "cupixworks-api",
  "occurrences": 1,
  "avg_ms": 1064,
  "max_ms": 1064,
  "sample_trace_id": "3703117997578681932"
}

Impact#

  • Service: cupixworks-api
  • 발생 횟수: 1
  • 최초 발생: 2026-05-26T23:39:23.662Z
  • 최근 발생: 2026-05-26T23:39:23.662Z
  • 영향: Octree 업로드 확인 API 호출 시 1초 이상 지연. 클라이언트가 업로드 완료 후 상태 전환을 확인하는 동안 사용자 경험 저하 가능.

Root Cause Summary#

check_octree_uploading 엔드포인트는 octree 파일의 업로드 완료 여부를 확인하기 위해 Aws::S3::Object#exists? (S3 HEAD 요청)를 매번 동기적으로 호출한다. S3 HEAD 요청은 네트워크 왕복 시간에 의존하며, 캐싱이나 타임아웃 제어 없이 직접 호출되고 있다. APM 메트릭에 따르면 이 엔드포인트의 평균 응답 시간은 300-450ms이며, 피크 시 700ms 이상에 도달한다. 1064ms는 S3 서비스 지연이 일시적으로 증가했을 때의 상위 백분위수 outlier로 판단된다.

Technical Analysis#

Code Path#

  • Entry point: app/controllers/concerns/octree_controller.rb:4
  • Repository: app/repositories/concerns/octree_repository.rb:4
  • Model method: app/models/concerns/octree.rb:26
  • Failure point (latency source): app/models/concerns/octree.rb:9
app/controllers/concerns/octree_controller.rb:4-9ruby
def check_octree_uploading
  repository_instance.check_octree_uploading
  render_api Renderable.new({
    contents: @model
  })
end
app/repositories/concerns/octree_repository.rb:4-13ruby
def check_octree_uploading
  case @model.octree_state_name
  when :uploading
    raise Cupix::Errors::Resource.new(code: 'RESC10000', reason: 'Octree does not uploaded') unless @model.check_octree_uploading
  else
    raise Cupix::Errors::InvalidState.new(code: 'STAT10000', reason: "Invalid state: #{@model.octree_state}")
  end

  @model
end
app/models/concerns/octree.rb:8-10ruby
def octree_uploaded?
  octree_object.exists?  # AWS S3 HEAD request - primary latency source
end
app/models/concerns/octree.rb:12-18ruby
def octree_object
  Cupix::StorageService.object(
    storage_option: storage_option,
    bucket_name: storage_option.s3_hosting_bucket_name,
    key: self.octree_object_key
  )
end
app/services/cupix/storage_service.rb:29-35ruby
def object(storage_option: nil, **kwargs)
  opts = parse_storage_option(storage_option).merge(kwargs)
  opts[:force_path_style] = true

  check_required_params(opts, %i[region bucket_name key])

  Aws::S3::Object.new(opts)
end

실행 흐름:

  1. Controller → Repository에서 octree_state_name:uploading인지 확인
  2. Model의 check_octree_uploadingoctree_uploaded? 호출
  3. octree_uploaded?octree_object.exists?를 호출 — 이것이 AWS S3 HEAD 요청
  4. storage_option 조회 시 Rails.cache 사용 (1주 TTL, storagable.rb:87), 그러나 S3 HEAD 결과는 캐싱 없음
  5. S3 HEAD 요청의 네트워크 왕복 시간이 전체 응답 지연의 주요 원인

Log Evidence#

사용한 Datadog 쿼리:

text
service:cupixworks-api "check_octree_uploading"
Time: 2026-05-26T22:30:00Z to 2026-05-27T00:30:00Z

로그에서 확인된 패턴 — 모든 요청이 200 OK로 응답:

json
{
  "timestamp": "2026-05-27 09:18:13 KST",
  "status": "info",
  "message": "[200] PUT /api/v1/pointclouds/117124/check_octree_uploading (Api::V1::PointcloudsController#check_octree_uploading)"
}

APM 메트릭 (최근 1시간, avg:trace.rack.request.duration):

text
Query: avg:trace.rack.request.duration{service:cupixworks-api,resource_name:api::v1::pointcloudscontroller_check_octree_uploading}
Results: 47 data points
- Min: 0.199s (199ms)
- Max: 0.783s (783ms)
- Avg: ~0.42s (420ms)
- Peak period: 0.7-0.78s at timestamps 1779838680000-1779838920000

1064ms의 단일 발생은 APM 메트릭의 최고치(783ms)보다 36% 높으며, S3 서비스 측 일시적 지연으로 인한 outlier로 판단된다.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 S3 HEAD 요청의 네트워크 지연으로 인한 latency spike APM 메트릭에서 평균 420ms, 최고 783ms 관측. octree.rb:9에서 매 요청마다 S3 HEAD 호출 확인. 캐싱 없음. Confirmed
H2 DB 쿼리 지연 (storage_option 조회) storagable.rb:87에서 Rails.cache.fetch 사용 (1주 TTL) 캐시 적중 시 DB 접근 없음. 캐시 만료 시에도 단순 조회로 수십ms 이하. S3 HEAD가 전체 시간의 대부분 차지. Rejected
H3 대용량 octree 파일로 인한 S3 처리 지연 S3 HEAD 요청은 파일 크기와 무관하게 메타데이터만 확인. 객체 크기는 HEAD latency에 영향 없음. Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • 필요 없음: 단일 발생(1건)이며 기능적 오류 없이 200 OK 반환. 1064ms는 UX에 영향이 있을 수 있으나 critical은 아님.

단기 개선 (1주 이내)#

  • app/models/concerns/octree.rb:8-10: octree_uploaded? 결과를 짧은 TTL(30-60초)로 인메모리 또는 Rails 캐시에 캐싱하는 방안 검토. 업로드 확인은 파일이 한번 존재하면 이후 항상 존재하므로, positive result는 영구 캐싱 가능.
  • S3 client에 타임아웃 설정 추가 검토 (Aws::S3::Client 초기화 시 :http_open_timeout, :http_read_timeout 옵션).

장기 개선: S3 Event Notification 전환#

S3 업로드 완료를 S3 Event Notification → SQS로 비동기 감지하는 방식으로 전환하면, 클라이언트의 polling 방식 HEAD 요청을 제거할 수 있다. compass-service에서 동일 패턴(aws_s3_bucket_notification → SQS → Lambda)을 이미 운영 중이므로 참조 가능.

변경 범위 분석#

영향받는 레포:

  • cupix-infrastructure (Terraform — S3/SQS 리소스)
  • tesla (cupixworks-api — Rails 코드)
  • 클라이언트 (optional — polling 제거 시)

Infrastructure 변경 (cupix-infrastructure):

파일 변경 내용
cupix-service/sqs/main.tf octree upload event용 SQS queue + DLQ 추가
cupix-service/s3/base-region/main.tf aws_s3_bucket_notification 리소스 추가 (hosting bucket → SQS, prefix filter: */octree/)
cupix-service/sqs/ (new) SQS queue policy (S3 → SQS SendMessage 허용)
IAM API 서비스에 sqs:ReceiveMessage, sqs:DeleteMessage 권한 추가

현재 cupix-infrastructure/cupix-service/s3/base-region/main.tf에는 event notification 설정이 전혀 없으며, hosting bucket은 14개 리전에 걸쳐 생성됨. 각 리전별 hosting bucket에 notification을 추가해야 한다.

참고 구현 — compass-service (cupixworks/applications/compass-service/src/lambda-function/main.tf:261-270):

terraform
resource "aws_s3_bucket_notification" "pano_vectorize_event" {
  bucket = aws_s3_bucket.analysis_model_bucket.id
  queue {
    queue_arn     = aws_sqs_queue.pano_vectorize_queue.arn
    events        = ["s3:ObjectCreated:*"]
    filter_prefix = "analyze_pano/"
    filter_suffix = ".json"
  }
}

API (tesla) 코드 변경:

파일 변경 내용
app/models/concerns/octree.rb check_octree_uploading 메서드를 DB 상태 확인 방식으로 변경 (S3 HEAD 제거)
app/workers/ (new) SQS consumer worker 추가 — S3 event에서 object key 파싱 → Pointcloud 식별 → uploaded_octree_state 호출
app/models/concerns/aws_adapter/sqs.rb 기존 SQS adapter 활용 가능 (이미 Aws::SQS::Client 래퍼 존재)
config/routes.rb check_octree_uploading 엔드포인트 유지 (backward compatibility) — DB 상태만 조회하도록 변경
app/controllers/concerns/octree_controller.rb 변경 없음 (repository 계층에서 처리)
app/repositories/concerns/octree_repository.rb check_octree_uploading에서 S3 HEAD 대신 DB octree_state 확인으로 변경

SQS Worker 처리 흐름:

  1. S3 ObjectCreated event → SQS queue 수신
  2. Worker가 S3 event의 s3.object.key에서 pointcloud ID 추출 (octree_object_key 패턴 역파싱)
  3. 해당 Pointcloud의 octree_state:uploaded로 전환
  4. 메시지 삭제

핵심 복잡도:

  • octree_object_key는 SHA1 hex prefix를 포함하므로 (octree_hex = Digest::SHA1.hexdigest(...)[:16]), S3 event의 key에서 pointcloud ID를 역추적하려면 DB lookup 또는 별도 매핑 테이블이 필요
  • 14개 리전 hosting bucket 각각에 notification 설정 필요
  • 기존 클라이언트의 polling API와의 병행 운영 기간 필요

예상 작업량: Medium (2-3 sprint)

  • Terraform 변경: 리전별 bucket notification + SQS queue (1 sprint)
  • API worker 구현 + key 역파싱 로직: (1 sprint)
  • 테스트 + 클라이언트 마이그레이션: (1 sprint)

Monitoring#

  • APM p95/p99 duration 모니터 설정:
text
avg:trace.rack.request.duration{service:cupixworks-api,resource_name:api::v1::pointcloudscontroller_check_octree_uploading} > 1.0
  • S3 HEAD 요청 실패율 모니터링 (현재는 에러 없이 지연만 발생)

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: 단기(캐싱) trivial / 장기(S3 event notification) medium
  • 단일 발생이며 기능적 장애 없음. S3 네트워크 지연에 의한 일시적 latency spike로, 구조적 결함보다는 인프라 특성에 가까움. 캐싱 도입으로 재발 빈도를 줄일 수 있으나 즉시 대응 필요성은 낮음.
  • S3 event notification 전환은 14개 리전 버킷 × infra/API 양측 변경이 필요하여 2-3 sprint 규모이나, compass-service 선례가 있어 패턴은 검증됨.

Revision History#

Revision 1#

Feedback: S3 event notification 방식으로 변경하려면 얼마나 변경이 필요한지 확인해

판정:

피드백 항목 판정 근거
S3 event notification 전환 범위 조사 수용 cupix-infrastructure/cupix-service/s3/base-region/main.tf에 event notification 설정 부재 확인. tesla/app/models/concerns/octree.rb:8-9에서 S3 HEAD 직접 호출 확인. cupixworks/applications/compass-service/src/lambda-function/main.tf:261-270에서 동일 패턴의 참조 구현 확인. tesla/app/models/concerns/aws_adapter/sqs.rb에서 기존 SQS adapter 존재 확인. 14개 리전 hosting bucket(cupix-infrastructure/cupix-service/s3/outputs.tf:2-19) 각각에 notification 추가 필요.

변경 사항:

  • Fix Recommendation의 "장기 개선" 섹션을 구체적인 변경 범위 분석으로 확장 (영향 레포, 파일별 변경 내용, 예상 작업량)
  • Risk Assessment에 장기 복잡도 반영

추가 조사 내용:

  • cupix-infrastructure/cupix-service/s3/base-region/main.tf — hosting bucket 정의 확인, event notification 미설정 확인
  • cupixworks/applications/compass-service/src/lambda-function/main.tf — S3 → SQS → Lambda 참조 패턴 확인
  • tesla/app/models/concerns/octree.rboctree_object_key 생성 로직 확인 (SHA1 prefix로 역파싱 복잡도 파악)
  • tesla/app/models/concerns/aws_adapter/sqs.rb — 기존 SQS client adapter 존재 확인
  • tesla/app/models/concerns/statable/pointcloud.rb:193-211 — octree state machine 확인
  • cupix-infrastructure/cupix-service/s3/outputs.tf — 14개 리전 hosting bucket 목록 확인