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#
- 2026-06-04 14:21 KST — Elasticsearch 클러스터 연결 장애 시작 (ap-southeast-2)
- 2026-06-04 14:21:10 KST —
SketchesController#show요청 수신, Elasticsearch timeout으로 10131ms 소요 - 2026-06-04 14:21:20~22 KST — 다수의 Elasticsearch timeout 에러 발생 (Pano, Group, ClusterRepository 등 12건+)
- 2026-06-04 14:22 KST — 정상 응답 재개 확인
Error Log#
{
"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.show가 User#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_sketch→repository_instance.show(params[:id])
def set_sketch
@model = repository_instance.show(params[:id], capture_id: params[:capture_id])
end
BaseRepository.showresolves model via complex permission JOINs, then callsset_current_workspace:
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:
update(current_workspace_id: workspace_id) if workspace_id.present?
- User model은
Searchable::User를 include하므로after_commit on: [:update]→_update_document실행:
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 설정:
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#
검색 쿼리:
service:cupixworks-api status:error "Operation timed out" from:2026-06-04T05:20:00Z to:2026-06-04T05:25:00Z
동일 시점에 다수의 Elasticsearch timeout 에러 발생 (12건):
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 응답 정상 확인:
service:cupixworks-api "SketchesController#show" from:2026-06-04T05:22:00Z
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_workspace → Workspace.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_document를perform_async로 변경하여 ES indexing을 항상 비동기 처리
- 방법 1:
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 에러 빈도 모니터링:
service:cupixworks-api status:error "Operation timed out" "milliseconds"
SketchesController#showP99 latency alert (threshold: 3000ms)- Elasticsearch 클러스터 상태 메트릭:
aws.es.cluster_status.red,aws.es.nodes_count
Risk Assessment#
- Risk level: low
- 예상 복잡도: standard — 단발성 인프라 이벤트이나, 구조적으로 read 요청에서 ES write가 발생하는 설계 문제가 있어 반복 가능성 존재