IL-0567 Devsisters · 2023-09-05 (게시일)databaseaws
쿠키런: 오븐브레이크의 패키지 매출 랭킹 기능을 배포한 뒤 Aurora MySQL CPU가 100%에 이르러 유저가 게임에 접속하지 못했다
2022년 8월에 추가된 쿠키런: 오븐브레이크의 패키지 매출 랭킹은 판매 중인 패키지의 지난 N시간 매출을 집계해 순위를 보여주는 기능이다. 구매 기록을 담은 AWS Aurora MySQL 테이블에 구매 시점 정보가 없어 purchase_dt 칼럼과 인덱스를 추가해야 했다. 일반 ALTER TABLE은 table lock을 잡는다. 게다가 이 테이블은 서비스 시작 이후의 모든 구매 기록을 담은 큰 테이블이라 2시간 점검 안에 끝내기도 어려웠다. 그래서 팀은 online alter를 택했다. lock을 잡지 않는 inplace algorithm은 테스트 서버 Database 복제본에서 첫 시도에 ERROR 1062 (23000): Duplicate entry '73817' for key 'PRIMARY' 오류로 실패했다. MySQL 공식 문서가 이를 간헐적으로 생기는 한계라고 설명하고 있어, 팀은 이 방식을 쓰지 않기로 했다. 대신 Percona Toolkit의 pt-online-schema-change를 Staging 복제본, Production 복제본, Staging, Production 순서로 시험하고 적용했다. Production 복제본에서는 CPU를 45% 정도로 유지하는 chunk-size 250을 골랐고, Production 적용은 문제 없이 끝났다.
기능 배포 당일 CPU가 조금 오른 데 이어, 버그 픽스를 더 배포하고 유저를 재접속시키자 CPU가 100%에 이르러 Database 접속이 실패했다. 팀은 서버 점검을 걸고, 랭킹 기능을 끄고 켤 수 있는 설정을 급히 넣어 꺼 둔 뒤 서비스를 정상화했다. 원인은 두 가지였다. 첫째, 쿼리가 product_id와 purchase_dt를 함께 조건으로 쓰는데도 둘을 묶은 인덱스가 없었고, 매번 바뀌는 현재 시각이 조건에 있어 Database가 최적화하기 어려웠다. composite index는 개선 폭이 10% 정도에 그쳐 넣지 않았다. 대신 판매 시작 시간이 같은 상품의 조건을 묶고 현재 시각 조건을 지운 쿼리로 바꿨다. 둘째, AWS autoscaling group으로 수평 분산된 서버마다 게임데이터 버전별로 캐시를 따로 만들어, 쿼리가 분산 서버 수 곱하기 게임데이터 가짓수만큼 실행됐다. 서버를 업데이트하면 캐시가 빈 서버가 늘어 부하가 더 커졌다.
팀은 스케줄러 서버가 모든 서버의 요청을 모아 주기적으로 쿼리하고 결과를 원격 저장소에 캐싱하는 공용 캐시를 만들었다. 각 서버는 Database 대신 원격 저장소의 결과를 읽는다. 이후 랭킹 기능을 다시 켰을 때 CPU는 스케줄러 서버의 작은 피크 말고는 안정적으로 유지됐다.
- 심각도
- –
- 영향 범위
- Cookie Run: OvenBreak players could not log in to or play the game while database connections failed
- 영향 사용자
- –
- 지역
- –
- 감지까지
- –
- 복구까지
- –
증상
패키지 매출 랭킹 기능을 배포한 뒤 버그 픽스를 몇 차례 더 배포하고 유저를 재접속시키자 Database 알람이 울렸고, Database CPU 그래프가 100%에서 내려오지 않았다. Database 접속에 문제가 생기면서 유저들이 게임에 정상적으로 접속하거나 플레이할 수 없었다.
배포 직후에는 평소 20% 정도이던 CPU가 10~20% 정도 올랐다. 개발자는 새 기능에 넣은 쿼리의 영향이라는 것을 알아챘지만, 아직 큰 문제는 아니라고 보고 함께 배포한 다른 기능의 문제부터 고쳤다.
각 서버는 게임데이터 버전마다 캐시를 따로 만들기 때문에, 서버를 업데이트하면 캐시가 빈 서버가 늘어 같은 집계 쿼리가 한꺼번에 많이 실행됐다. 개발 환경에서는 결제 데이터가 적어 쿼리가 빨랐고 분산 서버 수도 적어 이 문제가 드러나지 않았다.
원인
- 촉발
- 새 패키지 매출 랭킹 기능의 집계 쿼리가 들어간 배포 뒤 버그 픽스를 추가로 배포하고 유저를 재접속시키면서, 캐시가 빈 서버들이 이 쿼리를 한꺼번에 실행했다.
감지
버그 픽스를 추가로 배포하고 유저를 재접속시킨 뒤 Database 알람이 울려 CPU 그래프를 보고 알았다.
조치
- 서버 점검을 걸었다.
- 문제가 된 패키지 매출 랭킹 기능을 켜고 끌 수 있는 설정을 빠르게 추가해 꺼 둔 뒤 서비스를 정상화했다.
- 판매 시작 시간을 공유하는 상품들의 product_id 조건을 하나로 묶고, 의미가 없는 현재 시각 조건을 지운 쿼리로 바꿔 바로 적용했다.
- 모든 서버가 함께 쓰는 캐시를 스케줄러 서버에 따로 두었다. 각 서버는 Database 대신 원격 저장소의 결과를 읽고, 요청은 큐에 넣는다. 스케줄러 서버는 주기적으로 마지막 요청으로 쿼리해 결과를 원격 저장소에 캐싱한다. 이렇게 해서 쿼리 실행 횟수를 줄였다.
- 쿼리 최적화와 캐시 사용 방식 개선을 적용한 뒤 패키지 매출 랭킹 기능을 다시 켰고, CPU가 문제 없이 유지되는 것을 확인했다.
후속 조치
–
교훈
무거운 쿼리와 분산 서버의 캐시 구조가 겹치면 예상하지 못한 문제가 생기므로, 문제가 될 만한 지점을 미리 파악해야 한다. 장애가 나면 원인 파악보다 서비스 정상화가 먼저다. 문제가 되는 기능을 끄고 켤 수 있는 안전장치를 미리 만들어 두면 서비스를 더 안정적으로 운영하는 데 도움이 된다. Production 환경의 online alter에는 Percona Toolkit 같은 유용한 툴을 잘 활용하는 것이 중요하다.
출처
관련 사례
같은 원인 · 공통 데이터베이스
IL-0136Sweden Central 리전에서 내부 서비스 배포가 캐시를 비워 메타데이터 백엔드에 평소 20배의 트래픽이 몰렸고, 데이터베이스 연결에 기대는 헬스 체크가 인스턴스를 계속 재시작시켜 Azure OpenAI 요청이 6시간 가까이 실패했다Microsoft Azure · 2026같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0510MIT Learn 운영 RDS PostgreSQL 의 저장 공간이 임시 파일로 가득 차 learn.mit.edu 가 세 시간 동안 503 과 504 를 냈다MIT Open Learning · 2026같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0547Buildkite 의 Agent API, REST API, GraphQL 이 annotations 테이블 쿼리 타임아웃으로 4시간 가까이 저하됐다Buildkite · 2025같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0324External Storage 데이터베이스 클러스터에 과부하가 걸려 mw-api-int, mw-parsoid, mw-jobrunner 의 지연과 데이터베이스 오류가 늘었다Wikimedia Foundation · 2025같은 원인 · 공통 데이터베이스
IL-0307Supabase us-east-1 에서 Supavisor 2.0 업그레이드 뒤 Postgres 연결 풀러가 테넌트 약 80% 의 연결을 끊었다Supabase · 2025같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0523TANF-app 의 운영 배포가 PostgreSQL 마이그레이션 실패로 두 달 동안 막힘U.S. Department of Health and Human Services · 2023같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0524인덱스를 다시 만드는 마이그레이션이 운영에서 Transactions 테이블을 잠가 Open Collective 서비스가 느려짐Open Collective · 2023같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0567
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0567/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.