Incident Library

IL-0571 STORES · 2024-07-01 (게시일)database

STORES 予約의 스마트리스트 검색 쿼리가 특정 테넌트에서 20분 넘게 DB 프로세스를 붙잡아 504 에러가 급증했다

STORES 予約의 스마트리스트는 고객 검색 조건을 저장해 두고 고객 목록에서 조건에 맞는 고객에게 라벨을 붙이는 기능이다. 팀은 고객 목록을 검색할 때마다, 화면에 보이는 최대 50명만 대상으로 스마트리스트마다 DB를 검색해 라벨을 정하도록 만들었다. 출시 2주 전 QA에서 팀은 예약 페이지별 예약 횟수와 최종 예약일 검색이 의도와 다르게 동작한다는 것을 알았다. 그래서 고객과 예약 페이지별 예약 횟수와 최종 예약일을 모으는 인라인 뷰로 검색을 다시 만들었다. 고객 검색에는 옵티마이저 힌트 MAX_EXECUTION_TIME(1000) 을 넣었고, 검증 환경에서 EXPLAIN과 타임아웃을 확인한 뒤 운영 환경에 배포했다.

그 뒤 사양 변경에 맞춰 미리 만들어 둔 프리셋 데이터의 검색 조건을 고쳐 쓰자 프리셋이 새 검색 쿼리를 타게 됐다. 놓친 것은 세 가지였다. 스마트리스트 대상 고객을 찾는 쿼리에는 MAX_EXECUTION_TIME 힌트를 넣지 않았다. pluck 결과로 ID만 넘겨 힌트가 이어지지 않았기 때문이다. 바깥 쿼리는 고객 ID 50개로 좁혔지만 인라인 뷰 안쪽은 좁히지 않아 테넌트의 모든 고객을 훑었다. 프리셋 이름은 그대로라 엔지니어는 큰 변경이라고 생각하지 않았다. 대부분의 테넌트는 몇 초 안에 응답했지만, 특정 테넌트에서는 쿼리 하나가 20분 넘게 DB 프로세스를 붙잡아 서버가 막혔다.

EXPLAIN에서는 인덱스가 쓰이고 수만 분의 1로 좁혀지는 것처럼 보였다. 그러나 멀티테넌트 환경에서는 테넌트가 1만 개만 돼도 테넌트 ID 인덱스만으로 1만 분의 1로 좁혀지므로, 크게 좁혀진 것처럼 보여도 실제로는 상당한 행을 훑고 있었다. 동작 확인은 두 검색이 모두 통과하는 경우와 모두 실패하는 경우만 봤고, 첫 검색은 통과하고 스마트리스트 검색만 오래 걸리는 경우는 보지 않았다. 모니터링은 라이터 인스턴스만 봤는데 문제의 쿼리는 리더 인스턴스로 갔다. 막힌 요청은 지표를 보내기 전에 프로세스가 중지돼 원인을 찾는 데 시간이 걸렸다. 팀은 이후 DB 설계부터 다시 봤다. 스마트리스트 출시는 5월로 미뤄져 5월 6일에 나왔다.

심각도
–
영향 범위
Requests to STORES 予約 returned 504 errors (Gateway Timeout) while the database was stuck on long queries
영향 사용자
–
지역
–
감지까지
–
복구까지
–

증상

스마트리스트 프리셋 데이터를 고쳐 쓰는 배치를 20분쯤 돌린 시점에 504 Gateway Timeout 에러가 갑자기 늘었다는 알림이 왔다. 작업은 03:00 UTC 조금 넘어 시작했다.

작업 중에는 Web 서버 응답 지연과 쓰기용 DB의 부하 같은 지표를 보고 있었는데 큰 변화가 없었다. 문제의 쿼리는 리더 인스턴스로 갔기 때문에 모니터링하던 라이터 인스턴스의 부하는 전혀 오르지 않았다.

엔지니어들은 원인을 찾지 못한 채 직전 변경을 되돌렸지만 504 에러는 그치지 않았다. DB 부하가 높아 보여 프리셋 고쳐 쓰기 배치도 멈췄지만 역시 그치지 않았다.

막힌 요청의 지표도 볼 수 없었다. Rails의 DB 접근 지표는 요청 처리가 끝날 때 한꺼번에 보내는데, DB 응답을 20분 기다리던 프로세스가 멈춘 것으로 판정돼 지표를 보내기 전에 중지됐다. 이 때문에 원인을 찾는 데 시간이 더 걸렸다.

증상 오류율 상승데이터베이스 과부하

원인

촉발
검색 사양 변경에 맞춰 스마트리스트 프리셋 데이터인 初回予約の顧客 와 要フォロー顧客 의 검색 조건을 새 조건으로 고쳐 쓰는 배치를 실행했다. 프리셋 이름은 그대로였지만 검색 파라미터가 달라져 새 검색 쿼리를 타게 됐다.

감지

고쳐 쓰기 배치를 시작하고 20분쯤 지나 504 에러가 늘었다는 알림으로 알았다. 작업 중 보던 라이터 인스턴스 지표에는 변화가 없었다.

조치

–

후속 조치

교훈

실행 계획을 보고 실제 데이터로 동작을 확인해도, 멀티테넌트 환경에서는 테넌트 인덱스만으로 크게 좁혀지는 것처럼 보여 실제로 훑는 양을 놓칠 수 있다. 검증은 양극단만이 아니라 일부만 느린 경우까지 봐야 한다. 작업 중 모니터링하는 대상이 실제 부하가 걸리는 곳인지도 확인해야 한다. 일정을 너무 서두르면 이런 부분이 빠지기 쉽고, 결국 일정이 늦어지고 사용자에게 피해를 준다.

출처

관련 사례

내 클러스터에도 있는지 확인하기

kubegap 은 Claude Code 나 Codex 에서 쿠버네티스 클러스터를 읽기만 하며 이 사례와 같은 상태를 점검합니다. 이 사례의 구성 요소: 데이터베이스. 점검 항목의 근거는 이 사례 같은 공개 보고서입니다.

AI 도구와 스크립트용

API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.