사후 분석: 202 (새 탭에서 열림)
2020년 1월 21~22일 Figma 장애는 장시간 실행된 고비용 쿼리와 PostgreSQL의 잘못된 쿼리 실행 계획, 그리고 이로 인해 누적된 공격적 autovacuum이 복합적으로 발생한 사건이었다. 1월 21일에는 문제 쿼리를 취소해 복구했지만, 그 여파로 데이터베이스 정리 작업이 밀리면서 다음 날 쓰기 IOPS와 잠금 경합이 급증했다. Figma는 PostgreSQL 11로 업그레이드해 문제를 안정화했고, 이후 고비용 쿼리 모니터링과 실행 시간 제한을 강화하기로 했다. ## 장애 발생 경과 ### 1월 21일: 장시간 실행 쿼리 - 오전 6시 11분, 자동 모니터링에서 오류율 증가를 감지했다. - 조사 결과, 데이터베이스 CPU를 과도하게 사용하는 장시간 실행 쿼리가 발견됐다. - 오전 6시 54분 해당 쿼리를 취소하자 성능이 정상으로 돌아왔다. - 그러나 쿼리 취소 과정에서 데이터베이스 내부에 정리해야 할 작업이 누적되었고, 이것이 다음 날 장애의 배경이 됐다. ### 1월 22일: 쓰기 부하와 잠금 경합 - 데이터베이스 CPU는 평소보다 낮았지만 쓰기 IOPS와 잠금 경합이 증가했다. - 오전 11시 42분에는 API 요청이 대기열에 쌓이며 일부 사용자가 서비스를 이용할 수 없게 됐다. - 불필요한 쿼리를 취소하고 할당된 IOPS를 늘려 일시적으로 안정화했지만, 오후 2시경 다시 성능이 악화됐다. - 데이터베이스를 재시작해 문제를 일으킨 것으로 추정한 백그라운드 프로세스를 임시 비활성화했다. - 오후 7시부터 긴급 점검을 진행하며 PostgreSQL을 업그레이드했고, 오후 8시 15분 서비스와 데이터베이스 지표가 정상화됐다. ## 공격적 autovacuum의 영향 - PostgreSQL의 autovacuum은 오래된 행 버전을 정리하고 트랜잭션 ID 고갈을 방지하는 자동 유지보수 작업이다. - 1월 21일 장시간 쿼리가 종료된 뒤 정리 대상 데이터가 대량으로 쌓였다. - 이 backlog가 임계치를 넘으면서 트랜잭션 ID 래핑을 막기 위한 더 공격적인 autovacuum이 실행됐다. - 당시 사용하던 PostgreSQL 버전에서는 이 작업이 테이블 잠금과 쓰기 작업에 큰 영향을 줬다. - Figma가 대형 테이블에서 실행 중인 autovacuum을 취소하자 지표가 일시적으로 개선됐지만, 작업은 다시 재개됐다. - `autovacuum_freeze_max_age` 값을 조정하고 데이터베이스를 재시작해야 공격적 autovacuum을 완전히 억제할 수 있었다. - 다만 autovacuum을 비활성화한 뒤에도 쓰기 지연과 잠금 경합이 계속되어, 이것만이 유일한 원인은 아니라고 판단했다. ## 잘못된 쿼리 실행 계획 - 문제의 핵심 쿼리는 복잡한 서브쿼리를 포함하고 있었고, 잠금 경합에 반복적으로 관여했다. - PostgreSQL 9의 통계 정보가 변경된 뒤 쿼리 플래너가 비효율적인 실행 계획을 선택한 것으로 분석됐다. - 실행 계획은 실제 결과가 3개 행뿐인데도 2천만 개 이상의 행을 반환할 것으로 잘못 추정했다. - 그 결과 인덱스를 사용하지 않고 전체 테이블 스캔을 수행했다. - 처리 과정에서 임시 버퍼에 대량의 데이터를 기록해 높은 쓰기 IOPS와 임시 데이터 사용량을 유발했다. - 즉, 낮은 CPU 사용률만으로 데이터베이스 상태가 양호하다고 판단하기 어려웠으며, 쓰기 지연·잠금·임시 버퍼 사용량을 함께 살펴야 했다. ## PostgreSQL 11 업그레이드 - PostgreSQL 9.6 이후에는 autovacuum과 관련된 중요한 최적화가 포함됐다. - PostgreSQL 10 이상에서는 Amazon RDS의 성능 분석 도구가 개선되어 원인 조사에 도움이 된다. - Figma는 이미 스테이징 환경을 PostgreSQL 11로 업그레이드하고 수개월간 테스트한 상태였다. - 운영 환경 업그레이드 절차도 사전에 마련해 두었기 때문에 긴급 상황에서도 업그레이드를 진행할 수 있었다. - PostgreSQL 11의 개선된 쿼리 플래너는 문제가 된 실행 계획이 선택될 가능성을 제거했다. - autovacuum의 성능 특성도 PostgreSQL 9에서 11로 오면서 개선되어 두 가지 주요 원인을 함께 완화했다. ## 후속 조치 - 고비용·장시간 실행 쿼리에 대한 모니터링을 강화한다. - 쿼리가 실행될 수 있는 시간에 더 엄격한 제한을 둔다. - 실행 계획의 예상 행 수와 실제 행 수 사이의 큰 차이를 지속적으로 점검한다. - CPU뿐 아니라 쓰기 IOPS, 쓰기 지연, 잠금 경합, 임시 버퍼 사용량, autovacuum backlog를 함께 모니터링해야 한다. - 데이터베이스 버전 업그레이드는 장애 발생 후 처음 검토하기보다, 스테이징 테스트와 운영 전환 계획을 미리 준비해야 한다. 실무적으로는 장시간 쿼리에 타임아웃을 설정하고, 정기적으로 `EXPLAIN ANALYZE`와 실행 계획 변화를 검토하며, autovacuum 상태를 별도 지표로 관리하는 것이 중요하다. alc-vesm.