Incident Library

IL-0310 Hightouch · 2025-10-23databaseaws

Aurora PostgreSQL 의 수동 장애 조치가 스스로 되돌아가 원래 writer 가 남고 백엔드의 쓰기 쿼리가 실패했다

Hightouch Events 는 이벤트 처리에 Kafka 를 쓰고, 배치 메타데이터를 담는 큐 저장소로 Amazon Aurora PostgreSQL 을 쓴다. 10월 20일 us-east-1 AWS 장애 뒤 밀린 이벤트를 처리하면서 작은 Postgres 인스턴스가 한계에 몰렸고, 팀은 여유를 늘리려고 데이터베이스 크기를 키우기로 했다. Aurora 는 크기를 키울 때 장애 조치가 빨리 끝나므로, 팀은 10월 23일 이 작업을 예약된 점검 시간 없이 진행했다. 계획은 임시 reader 인 instance #3 을 더하고, 기존 reader 인 instance #2 를 목표 크기로 키워 장애 조치 우선순위를 가장 높게 준 뒤, 장애 조치로 instance #2 를 writer 로 올리는 것이었다. 예상 중단 시간은 15초 미만이었고, 같은 날 스테이징의 부하 시험에서는 이 절차가 문제없이 끝났다.

16:39 EDT 와 16:43 에 실행한 장애 조치는 두 번 모두 잠깐 승격됐다가 되돌아갔고, 원래 writer 인 instance #1 이 primary 로 남았다. AWS 에서는 클러스터가 정상으로 보였지만 백엔드 서비스는 read-only transaction 오류로 쓰기를 하지 못했고, 팀은 서비스를 재시작해 정상으로 돌렸다. 데이터베이스 로그에서는 두 인스턴스 모두 쓰기 쿼리를 처리하던 프로세스가 signal 9 로 죽고 storage runtime process crash 로 내려간 기록이 나왔다. 팀은 장애 조치 동안 Aurora 가 잠깐 두 인스턴스 모두에 쓰기를 허용했고, 분산 저장 계층이 동시 쓰기를 거부해 두 인스턴스가 모두 죽었다는 가설을 세웠다. 팀은 이것을 원래 writer 를 강등하고 새 writer 를 승격하는 단계와 새 writer 가 쓰기를 받는 단계 사이의 레이스 컨디션으로 보았다.

가설을 확인하려고 데이터베이스에 쓰는 서비스를 모두 스케일 다운한 뒤 장애 조치를 다시 실행했고, 이번에는 성공했다. AWS 는 내부 검토 뒤 근본 원인이 원래 writer 를 강등하는 과정의 내부 신호 문제였고, 그 때문에 장애 조치 뒤에도 writer 가 바뀌지 않았다고 확인했다. AWS 는 Hightouch 의 구성이나 사용 방식에 이 버그를 일으킬 특별한 점은 없었다고 했다. 수정은 AWS 로드맵에 있다. 그 전까지 AWS 가 권고하는 완화책은 장애 조치를 필요할 때만 쓰고 장애 조치 동안 데이터베이스에 쓰기가 실행되지 않게 하는 것이다. Hightouch 는 us-east-1 클러스터의 크기를 키우는 작업을 마쳤다.

심각도
–
영향 범위
Hightouch Events backend services could not run write queries during two failover attempts until they were restarted, and the planned database upsize failed until a controlled failover with all database writers scaled down
영향 사용자
–
지역
us-east-1
감지까지
–
복구까지
–

증상

인스턴스 크기를 키우려고 Aurora 클러스터에서 장애 조치를 실행했다. 새 writer 로 올라가던 instance #2 는 잠깐 승격됐다가 곧 되돌아갔고, 원래 writer 인 instance #1 이 primary 로 남았다. AWS 콘솔에서는 클러스터가 모두 정상으로 보였지만 백엔드 서비스는 쓰기 쿼리를 실행하지 못했다.

일부 파드의 백엔드 애플리케이션 로그에 DatabaseError: cannot execute UPDATE in a read-only transaction 오류가 찍혔다. 서비스를 재시작하자 오류가 사라졌다.

데이터베이스 로그를 받아 보니 승격되던 reader 와 원래 writer 양쪽에 같은 줄이 같은 순서로 찍혀 있었다. LOG: database system is ready to accept connections 가 찍힌 뒤 LOG: server process (PID 799) was terminated by signal 9: Killed 가 나왔고, 죽은 프로세스는 백엔드가 보낸 쓰기 쿼리를 실행하고 있었다. 이어서 FATAL: Can't handle storage runtime process crash 와 LOG: database system is shut down 이 찍혔다.

장애 조치 동안 read replica 인 instance #2 의 연결 수, 네트워크 트래픽, 커밋 처리량이 치솟았다. 앱에서 오는 읽기 쿼리 트래픽은 그대로였으므로, 늘어난 트래픽은 writer 에 연결되어야 할 백엔드 서비스에서 온 것이었다.

16:39 EDT 첫 시도에 이어 16:43 에 다시 장애 조치를 실행했지만 똑같이 잠깐 승격됐다가 바로 되돌아갔다. 5분 사이에 두 번 실패했고, 그동안 코드 변경, 특이한 쿼리, 트래픽 급증은 없었다.

팀은 먼저 절차에 실수가 있었는지 확인하고 같은 문제를 겪은 사례를 인터넷에서 찾았지만 아무것도 나오지 않았다. 같은 날 앞서 스테이징 환경의 부하 시험에서는 같은 절차가 문제없이 끝났다. 백엔드는 writer 를 가리키는 클러스터 엔드포인트로 접속한다. 팀은 세 가지 가능성을 따져 보았다. 클러스터가 연결을 끊지 않아 파드가 writer 변경을 몰랐을 가능성, 클러스터 엔드포인트가 reader 를 잘못 가리켰을 가능성, writer 에 연결됐지만 쓰기가 실행 중에 거부됐을 가능성이다. 팀은 이 가운데 뒤의 두 가지를 강하게 의심했다.

증상 장애 조치 실패쓰기 거부

원인

촉발
2025-10-20 us-east-1 AWS 장애 뒤 밀린 이벤트를 처리하며 작은 Postgres 인스턴스가 한계에 몰리자, Hightouch 는 10월 23일 예약된 점검 시간 없이 Aurora 인스턴스 크기를 키우는 작업을 했다. 크기를 키운 reader 를 writer 로 올리려고 16:39 EDT 에 장애 조치를 실행했다.

감지

장애 조치를 실행한 뒤 AWS 콘솔 페이지를 새로 고치자 원래 writer 인 instance #1 이 여전히 primary 로 보였고, 백엔드 서비스가 쓰기 쿼리를 실행하지 못했다. 팀은 Datadog 에서 인스턴스마다 쿼리를 지켜보고 있었고 RDS 의 데이터베이스 로그를 받아 볼 수 있었기 때문에, writer 가 잠깐 바뀌었다가 돌아간 것을 찾아낼 수 있었다.

조치

후속 조치

교훈

믿을 만한 서비스에서 하는 마이그레이션이라도 목표 상태, 처음 상태, 그 중간 어느 상태로든 끝날 수 있다. 그러므로 트래픽을 다른 곳으로 돌리고 의존 서비스의 짧은 중단을 견딜 준비를 해 둔다. writer 가 잠깐 바뀐 일은 인스턴스별 쿼리 모니터링과 데이터베이스 로그가 있어서 찾을 수 있었다. 큰 분산 시스템에서는 구성 요소 하나가 시스템에 미치는 영향을 격리해 두면 가동 시간과 유지 보수 모두에 도움이 된다. 스테이징의 부하 시험은 운영 환경을 그대로 재현하지 못했고, 이번 레이스 컨디션을 일으킨 조건도 재현하지 못했다.

출처

관련 사례

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

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

AI 도구와 스크립트용

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