Api::V1::FacilityTypesController#index (avg 13606ms, max 13606ms)
Fix Plan: FacilityTypesController#index latency (avg 13606ms)
RCA 의 "즉시 조치 (Critical)" 섹션 2개 항목만 구현. 단기/장기 개선 (permission_joins 재구성, ES 인덱스 값 사용, SLO 도입 등) 은 이 PR 범위에서 제외.
Changes#
tesla: app/repositories/base_repository.rb#
- What:
BaseRepository#search에서_search(query_option)실행 직후self.response.records가 empty 이면 permission_joins 를 실행하지 않고 즉시 emptySearchResult를 반환하도록 early return 추가. pagination metadata 는self.response의 total_entries/total_pages/per_page/previous_page/next_page 그대로 사용 (ES 결과 0건이므로 모두 0/nil 이어야 정상). - Why: RCA Failure point (Empty scope) — Datadog 관측상 toyoeng team 의 요청은 ES 결과 0건인데도
permission_joins의 11개 LEFT JOIN + GROUP BY 서브쿼리들이 db 8-10s 를 소비. records 가 비면 permission_joins 결과도 반드시 empty 이므로 실행 자체가 낭비. - Lines:
app/repositories/base_repository.rb:70-82사이에 5-15줄 추가.
tesla: app/repositories/facility_type_repository.rb#
- What:
FacilityTypeRepository#search를 override 하여super결과의 각 record 에facilities_count를 batch preload 로 계산해 assign. 구현:super(query_option)로 SearchResult 획득- 반환된 records 를 main_type 과 sub_type 으로 분리
- Sub-type records:
Facility.untrashed.where(facility_type_id: sub_type_ids, team_id: team_ids).group(:facility_type_id, :team_id).count로 한번에 카운트 - Main-type records: 각 main_type 의
subtree_ids(ancestry 라이브러리) 를 모아 한번의Facility.untrashed.where(facility_type_id: all_subtree_ids, team_id: team_ids).group(:facility_type_id, :team_id).count후, main_type 별 자신 subtree 에 속한 facility_type_id 들의 count 를 합산 - 각 record 에
record[:facilities_count] = computed로 assign (또는record.assign_attributes(facilities_count: computed)) →FacilityType#facilities_count의return self[:facilities_count] if self[:facilities_count].present?분기가 short-circuit 하여 SQL COUNT skip
- Why: RCA Failure point 2 — Serializer 가 매 row 마다
FacilityType#facilities_count호출 → 매번 SQL COUNT (main_type 은 subtree scan 포함). forida-demo entries=4 에서 serialization 51810ms (row 당 12.9s) 관찰. 한 번의 GROUP BY 쿼리로 축소해 O(1) db round-trip 으로 만듦. - Lines:
app/repositories/facility_type_repository.rb에def searchoverride (~30-45줄) 및 private helper 추가.
Acceptance Criteria#
-
git diff상app/repositories/base_repository.rb의search메서드에records.empty?guard early return 이 확인됨 -
git diff상app/repositories/facility_type_repository.rb에def searchoverride 가 존재하고super호출 후 record 에facilities_count를 assign 하는 로직이 확인됨 - facility_type_repository 의 batch preload 는 최대 2개의 GROUP BY 쿼리 (sub_type + main_type subtree) 로 완료 — row 당 개별 COUNT 가 없음 (diff 상 loop 내
.count없음) -
ruby -c로 두 파일 문법 통과 - BaseRepository#search 변경은 기존 review/review_id/capture 분기에는 영향을 주지 않음 (records.empty? 만 판정하는 최상위 guard)
Tests#
- 기존 테스트:
spec/repositories/facility_type_repository_spec.rb,spec/repositories/base_repository_spec.rb(존재하는 경우 통과해야 함) - 신규 테스트: 이번 PR 범위에서는 unit test 추가 없음 (agent env 에서 bundle exec rspec 실행 불가 — cupixworks-api 관례에 따라 최소 변경으로 유지). Rubocop 은
ruby -c로 syntax 검증만 수행. - Sorbet: 두 파일이
# typed:헤더를 갖는지 확인 후 필요 시 type annotation 준수.