ES /docs

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 를 실행하지 않고 즉시 empty SearchResult 를 반환하도록 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. 구현:
    1. super(query_option) 로 SearchResult 획득
    2. 반환된 records 를 main_type 과 sub_type 으로 분리
    3. Sub-type records: Facility.untrashed.where(facility_type_id: sub_type_ids, team_id: team_ids).group(:facility_type_id, :team_id).count 로 한번에 카운트
    4. 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 를 합산
    5. 각 record 에 record[:facilities_count] = computed 로 assign (또는 record.assign_attributes(facilities_count: computed)) → FacilityType#facilities_countreturn 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.rbdef search override (~30-45줄) 및 private helper 추가.

Acceptance Criteria#

  • git diffapp/repositories/base_repository.rbsearch 메서드에 records.empty? guard early return 이 확인됨
  • git diffapp/repositories/facility_type_repository.rbdef search override 가 존재하고 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 준수.