ES /docs

Api::V1::SketchesController#show (avg 10131ms, max 10131ms)

RCA: SketchesController#show Latency (10131ms)

Overview#

What Happened#

2026-06-04 14:21 KST, ap-southeast-2 리전의 cupixworks-api 서비스에서 Api::V1::SketchesController#show 요청이 10131ms 소요되었다. 동일 시점에 Elasticsearch 클러스터 연결 장애가 발생하여, 요청 처리 중 실행된 Elasticsearch indexing 작업이 10초 timeout까지 blocking되었다.

Quick Facts#

Field Value
resource_name Api::V1::SketchesController#show
top_frame app/models/concerns/searchable.rb:55 (_update_document)
env production, ap-southeast-2
avg_duration 10131ms

Timeline#

  1. 2026-06-04 14:21 KST — Elasticsearch 클러스터 연결 장애 시작 (ap-southeast-2)
  2. 2026-06-04 14:21:10 KSTSketchesController#show 요청 수신, Elasticsearch timeout으로 10131ms 소요
  3. 2026-06-04 14:21:20~22 KST — 다수의 Elasticsearch timeout 에러 발생 (Pano, Group, ClusterRepository 등 12건+)
  4. 2026-06-04 14:22 KST — 정상 응답 재개 확인

Error Log#

Datadog Logs

json
{
  "resource_name": "Api::V1::SketchesController#show",
  "service": "cupixworks-api",
  "occurrences": 1,
  "avg_ms": 10131,
  "max_ms": 10131,
  "sample_trace_id": "447254492941585420"
}

Impact#

  • Service: cupixworks-api
  • 발생 횟수: 1
  • 최초 발생: 2026-06-04 14:21 KST
  • 최근 발생: 2026-06-04 14:21 KST

Root Cause Summary#

SketchesController#show 요청 처리 중 BaseRepository.showUser#set_current_workspace를 호출하여 User 레코드를 update한다. 이 update의 after_commit 콜백으로 Searchable#_update_document가 Elasticsearch에 동기적으로 indexing 요청을 보내는데, 해당 시점에 ap-southeast-2 리전의 Elasticsearch 클러스터가 응답 불가 상태여서 Faraday HTTP 클라이언트의 10초 timeout까지 blocking되었다. Elasticsearch timeout 설정이 config/initializers/elasticsearch.rb:25에서 timeout: 10으로 지정되어 있어, 전체 요청 응답 시간이 ~10131ms로 기록되었다.

Technical Analysis#

Code Path#

  • Entry point: app/controllers/api/v1/sketches_controller.rb:10 (before_action :set_sketch)
  • set_sketchrepository_instance.show(params[:id])
app/controllers/api/v1/sketches_controller.rb:64-66ruby
def set_sketch
  @model = repository_instance.show(params[:id], capture_id: params[:capture_id])
end
  • BaseRepository.show resolves model via complex permission JOINs, then calls set_current_workspace:
app/repositories/base_repository.rb:363-365ruby
if current_user.present? && model.respond_to?(:workspace_id) && model.respond_to?(:team_id) && current_user.team_id == model.team_id
  current_user.set_current_workspace(model.workspace_id)
end
  • set_current_workspace가 User 레코드를 update:
app/models/concerns/properties/user.rb:58ruby
update(current_workspace_id: workspace_id) if workspace_id.present?
  • User model은 Searchable::User를 include하므로 after_commit on: [:update]_update_document 실행:
app/models/concerns/searchable.rb:55-121ruby
def _update_document
  # ... Elasticsearch indexing logic ...
rescue Faraday::TimeoutError => e
  Cupix::Logger.error("TimeoutError - #{e.message}", class: self.class.name, function: __method__)
  BulkIndexWorker.perform_async(self.class.name, [id], 'index')
end
  • Failure point: app/models/concerns/searchable.rb:94__elasticsearch__.client.update(...) 호출 시 Elasticsearch 무응답으로 10초 timeout 발생

  • Elasticsearch timeout 설정:

config/initializers/elasticsearch.rb:17-27ruby
Elasticsearch::Model.client = ConnectionPool::Wrapper.new(size: 10, timeout: 7) {
  Elasticsearch::Client.new(
    host: ENV.fetch('RAILS_ES_HOST') { 'localhost' },
    port: ENV.fetch('RAILS_ES_PORT') { DEFAULT_RAILS_ES_PORT },
    transport_options: {
      request: {
        timeout: 10
      }
    }
  )
}

Log Evidence#

검색 쿼리:

text
service:cupixworks-api status:error "Operation timed out" from:2026-06-04T05:20:00Z to:2026-06-04T05:25:00Z

동일 시점에 다수의 Elasticsearch timeout 에러 발생 (12건):

text
2026-06-04 14:21:20 - TimeoutError - Operation timed out after 10001 milliseconds with 0 bytes received (Pano, _update_document)
2026-06-04 14:21:20 - TimeoutError - Operation timed out after 10002 milliseconds with 0 bytes received (Pano, _update_document)
2026-06-04 14:21:21 - Index error - Operation timed out after 10002 milliseconds with 0 bytes received (Group, _index_document)
2026-06-04 14:21:21 - TimeoutError - Operation timed out after 10002 milliseconds with 0 bytes received (Group, _update_document)
2026-06-04 14:21:21 - TimeoutError - Operation timed out after 10002 milliseconds with 0 bytes received (Pano, _update_document)
2026-06-04 14:21:21 - Index error - Operation timed out after 10002 milliseconds with 0 bytes received (Pano, _index_document)
2026-06-04 14:21:22 - Operation timed out after 10002 milliseconds with 0 bytes received (ClusterRepository)
2026-06-04 14:21:22 - Index error - Operation timed out after 10002 milliseconds with 0 bytes received (Pano, _index_document)
2026-06-04 14:21:22 - TimeoutError - Operation timed out after 10002 milliseconds with 0 bytes received (Pano, _update_document)

14:22 KST 이후 SketchesController#show 응답 정상 확인:

text
service:cupixworks-api "SketchesController#show" from:2026-06-04T05:22:00Z
text
2026-06-04 14:22:41 - [200] GET /api/v1/sketches/80434 (Api::V1::SketchesController#show)
2026-06-04 14:22:41 - [200] GET /api/v1/sketches/80433 (Api::V1::SketchesController#show)
2026-06-04 14:22:43 - [200] GET /api/v1/sketches/12665 (Api::V1::SketchesController#show)

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 Elasticsearch 클러스터 일시적 장애로 after_commit indexing 콜백이 10초 blocking 동일 시점 12건+ timeout 에러 (Pano, Group, Cluster 등 다수 모델), timeout 값 10s = trace duration 10.1s, _update_document rescue Faraday::TimeoutError 로그 패턴 일치 Confirmed
H2 SQL 쿼리 성능 저하 (복잡한 permission JOIN) permission_joins 메서드에 10+ LEFT JOIN 존재 10초 정도의 SQL 지연은 비정상적, 다른 show 요청은 같은 시간대에 정상 응답, timeout 에러 메시지가 ES 관련 Rejected
H3 set_current_workspaceWorkspace.find 에서 DB deadlock/lock wait User update가 존재하므로 lock 가능성 있음 에러 메시지가 "Operation timed out after 10002 milliseconds with 0 bytes received"로 네트워크 레벨 timeout, DB lock이면 다른 에러 패턴 Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • 현재 이슈는 일시적인 Elasticsearch 인프라 장애로 인한 단발성 이벤트. AP-Southeast-2 리전의 ES 클러스터 상태를 확인하고, 반복 발생 시 AWS 지원 티켓 검토.

단기 개선 (1주 이내)#

  • BaseRepository.show (line 363-364)의 set_current_workspace 호출을 비동기로 변경하여 read 요청에서 ES indexing이 응답 시간에 영향을 주지 않도록 개선.
    • 방법 1: set_current_workspace 내의 update 호출을 Sidekiq worker로 이전
    • 방법 2: after_commit 콜백의 _update_documentperform_async로 변경하여 ES indexing을 항상 비동기 처리
  • config/initializers/elasticsearch.rb:25의 timeout을 5초로 단축하여 ES 장애 시 최대 blocking 시간을 줄이는 것 검토.

장기 개선 (재발 방지)#

  • read 요청 (show/index)에서 write side-effect (set_current_workspace → User update)를 제거하는 아키텍처 변경. Read 요청은 DB/ES write 없이 순수 read로 동작해야 함.
  • Elasticsearch indexing을 모든 경우에 비동기(Sidekiq)로 처리하여, ES 장애가 API 응답 시간에 영향을 미치지 않도록 구조 개선.

Monitoring#

  • ES timeout 에러 빈도 모니터링:
text
service:cupixworks-api status:error "Operation timed out" "milliseconds"
  • SketchesController#show P99 latency alert (threshold: 3000ms)
  • Elasticsearch 클러스터 상태 메트릭: aws.es.cluster_status.red, aws.es.nodes_count

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: standard — 단발성 인프라 이벤트이나, 구조적으로 read 요청에서 ES write가 발생하는 설계 문제가 있어 반복 가능성 존재