IL-0510 MIT Open Learning · 2026-03-24databaseaws
MIT Learn 운영 RDS PostgreSQL 의 저장 공간이 임시 파일로 가득 차 learn.mit.edu 가 세 시간 동안 503 과 504 를 냈다
학습 자료 API, 그중에서도 추천 자료 API 가 느렸고, Sentry 는 조회 이벤트를 전부 가져와 건수를 세는 큰 쿼리를 가장 유력한 원인으로 지목했다. 이를 고치려는 쿼리 패턴 변경이 시니어 엔지니어 두 명의 리뷰를 거쳐 정기 릴리스로 배포됐다. 변경은 메인 쿼리 안에 집계를 넣었는데, DRF 가 페이지 응답의 건수를 구하려고 그 쿼리 전체를 COUNT 로 감쌌다. 로컬과 RC 환경처럼 데이터가 작은 곳에서는 문제가 없었지만, 운영에서는 Postgres 서버의 임시 파일이 저장 공간을 모두 채웠다. 18:35 UTC 에 시작된 이 상태에서 WAL 과 체크포인트가 밀리며 새 트랜잭션을 처리하지 못했고, learn.mit.edu 는 503 과 504 를 냈다.
팀은 Qdrant, pgdump 스냅샷 순으로 의심하다가 릴리스의 쿼리 변경을 원인으로 확인하고 롤백했다. 데이터베이스가 안정됐다고 본 뒤에도 503 이 다시 났다. 읽기 복제본 삭제, 복제 슬롯 삭제, VACUUM ANALYZE 를 차례로 했지만 DiskQueueDepth 는 계속 올랐다. AWS 지원과 연결된 상태에서 백업이 끝났고, 엔지니어가 10분 넘게 돌던 쿼리를 pg_terminate_backend 로 종료하자 디스크 큐 깊이와 남은 저장 공간이 정상으로 돌아왔다. 장애는 모두 3시간 35초 동안 이어졌다.
그 뒤 팀은 조회 수를 미리 계산해 둔 열에서 읽도록 고쳐 배포했고, 영향을 받던 API 의 운영 응답 시간이 약 13~15초에서 150~200ms 로 줄었다.
- 심각도
- –
- 영향 범위
- learn.mit.edu returned 503 and 504 errors while the production database was out of storage
- 영향 사용자
- –
- 지역
- us-east-1
- 감지까지
- –
- 복구까지
- 3h
증상
learn.mit.edu 가 멈췄다는 보고가 Sentry 이슈 링크와 함께 올라왔다. MIT Learn 의 운영 데이터베이스인 RDS PostgreSQL 인스턴스가 쓸 수 있는 저장 공간을 모두 써 버린 상태였고, 사이트는 503 과 504 오류를 냈다.
엔지니어 한 명이 FreeStorageSpace 지표를 공유했고, 저장 공간을 먹기 시작한 시점은 18:35 UTC 였다. 문제의 중심은 Postgres 데이터베이스 서버가 임시 파일 시스템에 쓰기와 읽기를 끝없이 되풀이한 것이었다. Sentry 에는 504 가 많이 쌓였다.
데이터베이스가 올라와 안정됐다고 선언한 지 20분 뒤에 learn.mit.edu 에서 다시 503 이 났다. 그 뒤 CheckpointLag 지표가 위험해 보였고, 사이트가 돌아왔다는 첫 보고가 나온 뒤에도 RDS 상태를 보는 핵심 지표인 DiskQueueDepth 는 계속 나빴다.
처음에는 Qdrant 클러스터를 의심했지만 곧 운영 데이터베이스 문제로 확인했다. 다음에는 한 엔지니어가 직전에 만든 pgdump 스냅샷을 의심했다가 원인이 아니라고 판단했다. 이어서 방금 배포한 릴리스에 들어간 쿼리 패턴 변경이 의심을 받았고, 변경을 쓴 엔지니어가 원인으로 확인해 롤백했다.
롤백 뒤에도 바로 낫지 않아 여러 조치를 해 봤다. 버퍼에 쌓인 WAL 파일을 비울 여유를 주려고 읽기 복제본을 지웠고, 정리를 빠르게 하려고 복제 슬롯을 지웠지만 효과는 거의 없었다. VACUUM ANALYZE 로 공간을 되찾으려 했으나 DiskQueueDepth 는 계속 올랐고, 이 명령이 쿼리를 더 일으켜 오히려 문제를 키웠을 수 있다.
원인
- 촉발
- 정기 릴리스 절차로 배포한 변경이 페이지 응답의 메인 쿼리 안에 집계를 넣었다. DRF 는 페이지 응답의 count 칸을 SELECT COUNT * ($QUERY) 로 계산하므로, 운영의 데이터 규모에서 이 쿼리가 임시 파일을 대량으로 만들었다.
감지
엔지니어가 장애를 보고하며 Sentry 이슈를 연결했다. 이어 다른 엔지니어가 운영 데이터베이스의 저장 공간이 바닥난 것을 확인했고, FreeStorageSpace 지표와 Sentry 에 쌓인 504 도 같은 상황을 보여 줬다.
조치
- 쿼리 패턴 변경을 쓴 엔지니어가 그 변경이 원인임을 확인하고 릴리스를 롤백했다.
- 데이터베이스가 버퍼에 쌓인 WAL 파일을 따라잡아 비울 여유를 주려고 읽기 복제본을 삭제했다.
- AWS 지원의 도움을 받아 백업이 끝난 뒤 오래 돌던 쿼리를 안전하게 종료했다. pg_stat_activity 에서 state = 'active' 이고 시작한 지 10분이 넘은 쿼리를 pg_terminate_backend(pid) 로 끝내자 저장 공간과 처리 여유가 돌아왔다.
후속 조치
- 운영 learn 데이터베이스에 enhanced analytics 를 켜는 방안을 검토한다.
- 플랫폼 엔지니어링 문서 사이트에 롤백 절차를 적는다.
- 장기적으로는 새로 만드는 릴리스 관리 체계에서 롤백을 쉽게 할 수 있게 한다.
- 운영 데이터베이스의 DiskQueueDepth 에 알림을 걸되 너무 자주 울리지 않게 한다.
- API 와 사이트의 응답 시간이 평소보다 30% 넘게 높아지면 알림을 보낸다.
- 운영 데이터베이스 장애를 예제로 삼아 게임데이 훈련을 한다.
- 장애 관리 도구 Rootly 를 써서 장애 때 소통을 한 채널에 자동으로 모으고 장애 기록을 자동으로 남긴다.
- 장애 대응 런북을 가볍게 만든다. 장애가 나면 Zoom 통화를 열고, 당번 엔지니어가 지표가 평소 수준으로 돌아오는지 지켜보며, 소통 담당을 따로 두어 이해관계자의 문의가 핵심 대응자를 방해하지 않게 한다.
- 오래 도는 쿼리를 종료하는 SQL 과, 이 SQL 을 고쳐 아주 오래 도는 쿼리만 골라 종료하는 SQL 을 운영 Postgres 런북에 넣는다.
- Learn Resource View Event 모델이 지금은 건수를 세는 데만 쓰이므로 다시 설계해야 한다는 이슈를 쓴다.
- 익명화한 운영 데이터 생성 도구로 RC 환경의 데이터 양을 운영 수준까지 늘린다. 운영 데이터를 익명화해 담은 운영 규모의 부하 시험 환경을 만들고, 이해관계자도 시험에 쓰는 RC 에 영향이 없도록 인프라를 분리한다.
- 조회 수를 미리 계산해 둔 열에서 읽도록 고쳐 배포했다. 영향을 받던 API 의 운영 응답 시간이 약 13~15초에서 150~200ms 로 줄었다.
교훈
이 변경은 가장 시니어인 엔지니어 두 명이 꼼꼼히 리뷰했지만 로컬과 RC 환경에서는 아무 문제도 드러나지 않았다. 운영의 데이터 규모와 시스템 부하가 크게 달랐고, 효율적이라고 여긴 COUNT 집계가 디스크 사용량을 폭증시킬 것이라는 점은 당시 시스템 상태만 보고는 알기 어려웠다. 그래서 운영과 같은 규모의 데이터로 부하를 시험할 환경이 필요하다고 팀은 결론지었다.
AWS 지원은 ORM 쿼리 변경을 운영 수준의 데이터를 담은 스테이징에서 시험하고, EXPLAIN ANALYZE 로 실행 계획과 예상 자원 사용을 보며, 시험 중 log_temp_files 로 임시 파일 생성을 지켜보라고 권했다. 폭주하는 쿼리를 막도록 temp_file_limit 를 설정하고, 실행 계획에 따라 work_mem 이나 shared_buffers 를 조정하되 메모리를 너무 쓰면 out of memory 로 장애 조치가 일어날 수 있다는 점도 짚었다. 임시 파일을 많이 쓰는 작업에는 임시 파일을 EBS 대신 로컬 NVMe 에 두는 RDS Optimized Reads 를 고려하라고 했다.
출처
참고 문서
관련 사례
같은 원인 · 공통 데이터베이스
IL-0136Sweden Central 리전에서 내부 서비스 배포가 캐시를 비워 메타데이터 백엔드에 평소 20배의 트래픽이 몰렸고, 데이터베이스 연결에 기대는 헬스 체크가 인스턴스를 계속 재시작시켜 Azure OpenAI 요청이 6시간 가까이 실패했다Microsoft Azure · 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-0567쿠키런: 오븐브레이크의 패키지 매출 랭킹 기능을 배포한 뒤 Aurora MySQL CPU가 100%에 이르러 유저가 게임에 접속하지 못했다Devsisters · 2023같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
IL-0524인덱스를 다시 만드는 마이그레이션이 운영에서 Transactions 테이블을 잠가 Open Collective 서비스가 느려짐Open Collective · 2023같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
같은 원인 · 공통 데이터베이스
내 클러스터에도 있는지 확인하기
AI 도구와 스크립트용
- 이 사례의 JSON: https://incidentlib-api.benchlet.com/cases/IL-0510
- 가장 비슷한 사례들: https://incidentlib-api.benchlet.com/cases/IL-0510/similar
- MCP 서버(Streamable HTTP, 키 없음):
https://incidentlib-mcp.benchlet.com/mcp
API 는 키가 필요 없습니다. MCP 서버는 search_incidents, get_incident, list_incidents, get_topic, vocab 도구를 내놓습니다. 사용법은 API 와 MCP.