Api::V1::FloorplanSourcesController#download_single_resource (avg 14243ms, max 14243ms)
RCA: FloorplanSourcesController#download_single_resource Latency (14.2s)
Overview#
What Happened#
2026-06-03 21:06 KST에 cupixworks-api 서비스의 Api::V1::FloorplanSourcesController#download_single_resource 엔드포인트에서 단일 요청이 14,243ms 소요되었다. 동일 리소스에 대한 정상 요청이 38-80ms에 처리되는 것과 비교하면 약 180배 느린 응답이다. 원인은 동시 floorplan 생성/업데이트 작업으로 인한 DB 락 대기(10.6초)이다.
Quick Facts#
| Field | Value |
|---|---|
| resource_name | Api::V1::FloorplanSourcesController#download_single_resource |
| top_frame | app/controllers/concerns/single_resourcable_controller.rb:10 |
| env | production, us-west-2 |
| deploy | production-us-west-2-20260603t0634z0-bfe9a388-cupixworks |
| duration | 14,243ms (DB: 10,596ms) |
Timeline#
- 2026-06-03 21:05 KST — 동일 trace에서 floorplan 생성 (
POST /api/v1/floorplans) 시작, Facility 9591 관련 트랜잭션 수행 - 2026-06-03 21:06 KST —
download_single_resource요청 도달, DB 락 대기 시작 - 2026-06-03 21:06:57 KST — 요청 완료 (302 redirect), 총 14,144ms 소요 (DB 10,596ms)
- 2026-06-03 21:08 KST — 동일 trace 내 후속 floorplan 업데이트 및 tile upload 완료
Error Log#
{
"resource_name": "Api::V1::FloorplanSourcesController#download_single_resource",
"service": "cupixworks-api",
"occurrences": 1,
"avg_ms": 14243,
"max_ms": 14243,
"sample_trace_id": "1218735581250569910"
}
Impact#
- Service:
cupixworks-api - 발생 횟수: 1
- 최초 발생: 2026-06-03 21:06 KST
- 최근 발생: 2026-06-03 21:06 KST
- 영향 사용자: Gregory Morel (nestle/344 team, user ID 15505)
- 영향 리소스: FloorplanSource ID 35498, Facility 9591
Root Cause Summary#
download_single_resource 요청이 set_floorplan_source before_action에서 FloorplanSourceRepository.show()를 호출할 때, 동일 facility(9591)에 대한 동시 floorplan 생성/업데이트 트랜잭션이 DB row lock을 점유하고 있어 10.6초 동안 락 대기가 발생했다. FloorplanSourceRepository.permission_joins는 8개 LEFT JOIN과 facility_permissions, workspace_permissions, team_permissions 테이블을 조회하는 복합 쿼리로, 동시 트랜잭션이 관련 row에 exclusive lock을 보유하면 읽기 쿼리도 대기하게 된다.
Technical Analysis#
Code Path#
- Entry point:
app/controllers/api/v1/floorplan_sources_controller.rb:59 set_floorplan_source가repository_instance.show(params[:id])호출BaseRepository#show→FloorplanSourceRepository.show(class method)FloorplanSourceRepository.permission_joins실행 — 8개 LEFT JOIN으로 권한 체크- 이 쿼리가 10,596ms 대기 (lock contention)
- 쿼리 완료 후
set_single_resource에서@resource = @model.resource설정 download_single_resource에서@resource.download_url호출 → S3 presigned URL 생성 → 302 redirect
def set_floorplan_source
@model = repository_instance.show(params[:floorplan_source_id] || params[:id])
end
def download_single_resource
if @resource.revision == 0
raise Cupix::Errors::Resource.new(code: 'ENT10011', reason: "Resource does not uploaded: #{@resource.revision}")
else
download_opts = {}
download_opts[:filename] = params[:filename] if params[:filename].present?
redirect_to @resource.download_url(download_opts), allow_other_host: true
end
end
def self.default_joins(record)
record.includes(:storage).joins(:facility, :workspace)
end
record.joins("
LEFT JOIN (
SELECT facility_id, permission
FROM facility_permissions
WHERE facility_permissions.accessor_id = #{sanitized_user_id}
AND facility_permissions.accessor_type = 'User'
) AS facility_user_permissions
ON facility_user_permissions.facility_id = floorplan_sources.facility_id
-- ... 7 more LEFT JOINs on facility_permissions, workspace_permissions, team_permissions ...
").group('id').select(_select).where("
(GREATEST(...) = 1 AND GREATEST(...) > 0) OR GREATEST(...) > 1
")
DB 쿼리가 floorplan_sources 테이블을 joins(:facility, :workspace)로 조회하면서, 동시 트랜잭션이 해당 facility/workspace row에 exclusive lock을 보유 중이면 InnoDB에서 읽기 대기(lock wait)가 발생한다. 특히 MySQL의 기본 innodb_lock_wait_timeout은 50초이므로, 10초 대기 후 동시 트랜잭션이 커밋되면서 lock이 해제된 것으로 보인다.
Log Evidence#
Datadog에서 동일 trace (ID: 1218735581250569910)의 로그를 조회한 결과, 해당 trace는 floorplan 생성부터 download까지 전체 워크플로우를 포함:
Query: trace_id:1218735581250569910
Time window: 2026-06-03T12:05:22Z ~ 2026-06-03T12:09:12Z
핵심 로그:
2026-06-03T12:05:22.940Z - POST /api/v1/floorplans (Floorplan 생성, Facility 9591)
2026-06-03T12:06:42.975Z - GET download_single_resource (FloorplanSource 35497) → 38.22ms, DB 6.92ms ✓ 정상
2026-06-03T12:06:45.063Z - GET download_single_resource (FloorplanSource 35497) → 79.34ms, DB 32.8ms ✓ 정상
2026-06-03T12:06:46.976Z - GET download_single_resource (FloorplanSource 35498) → 39.44ms, DB 6.15ms ✓ 정상 (다른 호스트)
2026-06-03T12:06:57.073Z - GET download_single_resource (FloorplanSource 35498) → 14144.3ms, DB 10596.13ms ✗ 문제 발생
2026-06-03T12:08:28.113Z - GET download_single_resource (FloorplanSource 35498) → 64.89ms, DB 9.16ms ✓ 정상 (이후 해소)
문제 요청의 상세 정보:
{
"host": "ip-10-1-80-134.us-west-2.compute.internal",
"duration_ms": 14144.3,
"db_ms": 10596.13,
"http_status": 302,
"user": "gregory.morel1@fr.nestle.com",
"user_id": 15505,
"team": "nestle/344",
"resource_id": 35498,
"redirect_location": "https://s3.eu-west-3.amazonaws.com/cupixworks-source-0c24fc2dfb77-euwe3/resources/w3hvo2/euwe3/v1"
}
동일 시간대 error-level 로그는 0건. 문제 해소 후 동일 리소스에 대한 요청은 즉시 정상 복귀 (64.89ms).
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | DB lock contention으로 인한 쿼리 대기 — 동시 floorplan 생성 트랜잭션이 facility/workspace row에 exclusive lock을 보유 | DB time 10,596ms (전체 14,144ms 중 75%), 동일 trace에서 floorplan 생성(12:05:22Z) 진행 중, 이후 요청(12:08:28Z)은 즉시 정상 | — | Confirmed |
| H2 | S3 presigned URL 생성 지연 (AWS API latency) | 총 시간 14,144ms 중 DB 외 시간 약 3,548ms | DB time이 대부분(75%)을 차지, S3는 redirect URL만 생성하므로 네트워크 호출 최소, 다른 정상 요청에서도 동일 S3 버킷 사용 | Rejected |
| H3 | permission_joins 쿼리 자체의 비효율 (8개 LEFT JOIN) |
복잡한 쿼리 구조, 정상 시에도 6-33ms 소요 | 정상 요청은 6-33ms로 충분히 빠름, 쿼리 구조가 변경된 적 없음 | Rejected |
| H4 | Host-specific 이슈 (ip-10-1-80-134의 리소스 문제) | 문제 요청과 두 번째로 느린 요청(79ms) 모두 동일 호스트 | 79ms도 정상 범위, 이후 다른 호스트에서도 정상 | Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
이 이슈는 일회성 lock contention으로, 현재 시스템에 지속적 영향 없음. 즉각적인 코드 수정은 불필요하다.
단기 개선 (1주 이내)#
set_floorplan_source에서show()호출 시download_single_resource액션의 경우 권한 체크를 단순화하거나, read-only 쿼리에 statement timeout을 설정- 파일:
app/controllers/api/v1/floorplan_sources_controller.rb:59-61 - 방향: download 액션은 별도의 lightweight 쿼리(permission join 생략,
find_by(id:)+ 별도 권한 체크)를 사용하여 lock 대기 가능성 감소
- 파일:
장기 개선 (재발 방지)#
- Floorplan 생성/업데이트 시 facility/workspace 테이블에 대한 lock 범위를 최소화 (advisory lock 또는 optimistic locking 도입)
download_single_resource같은 읽기 전용 엔드포인트에 read replica 사용 검토- MySQL statement timeout (
SET SESSION MAX_EXECUTION_TIME = 5000) 적용하여 10초 이상 대기 방지
Monitoring#
download_single_resource엔드포인트 p99 latency 모니터링 추가- DB query time > 5000ms인 요청에 대한 alert 설정
service:cupixworks-api resource_name:"Api::V1::FloorplanSourcesController#download_single_resource" @duration:>5000ms
- InnoDB lock wait 모니터링:
innodb_row_lock_waits메트릭
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial
- 단일 발생 이벤트로, 동시 작업 패턴이 반복되지 않는 한 재발 가능성 낮음. 동일 facility에 대한 대량 batch 작업 시 유사 패턴 발생 가능.