{"id":"IL-0331","org":"Razorpay","date":"2020-06-08","docs":[],"title":{"en":"An RDS MySQL Multi-AZ failover lost 5 seconds of committed data and broke replication on all five read replicas","ko":"RDS MySQL Multi-AZ 장애 조치 뒤 읽기 복제본 다섯 대의 복제가 모두 깨지고 데이터 5초 분량이 사라졌다"},"domain":["database","aws"],"impact":{"scope":"The API application could not reach its RDS MySQL database for about 110 seconds and returned 5xx errors; replication failed on all five read replicas, whose traffic ran on the master for about six hours; about 5 seconds of transactional data were missing from RDS until corrected by hand","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"With innodb_flush_log_at_trx_commit set to 2, Razorpay had been open to data loss on any failover all along, even though RDS Multi-AZ copies everything on the primary's disk to the standby. The incident also taught the team how RDS runs a Multi-AZ setup and its failovers.","ko":"잘못 설정한 변수 하나 때문에 Razorpay 는 처음부터 데이터 손실 위험을 안고 있었다. 팀은 장애를 끝까지 파고들어 근본 원인을 찾았고, 그 과정에서 RDS 가 Multi-AZ 구성과 장애 조치를 어떻게 다루는지 많이 배웠다."},"related":[],"sources":[{"url":"https://razorpay.com/blog/day-of-rds-multi-az-failover/","kind":"blog","archived":"https://web.archive.org/web/20260418102237/https://razorpay.com/blog/day-of-rds-multi-az-failover/"}],"summary":{"en":"In an RDS Multi-AZ setup, the RDS endpoint points to a primary instance and a standby in another availability zone keeps MySQL shut down while the primary's EBS volume is copied synchronously to the standby's EBS volume. On failover, networking on the primary is cut, MySQL starts on the standby, the endpoint switches to it and the application reconnects; the old primary is then hard-rebooted onto a new host and EBS sync starts toward the new standby. Data reaches the standby only once it is on the primary's EBS volume. Razorpay had set innodb_flush_log_at_trx_commit to 2, so the InnoDB log was written at every commit but flushed to disk only once a second; the RDS default of 1, which flushes at every commit, is required for full ACID compliance. sync_binlog was set to 1, so every commit was flushed to the binary log at once.\n\nWhen the master failed over in December 2019, transactions from the last 5 seconds had been committed and written to the binary log but not flushed to the primary's EBS volume, so the new primary never had them. The five read replicas had already applied those rows from the binary log. The application then wrote new rows on the new primary and reused auto-increment ids such as X+1 that the replicas already held, and replication on every replica stopped on duplicate-key errors. Razorpay moved replica traffic to the master and recreated all five replicas, waiting six hours for the first, full snapshot from the new machine.\n\nThe next day, trace logs showed records missing from the database. Razorpay pulled the exact queries from the binary logs, followed the sequence of events in the application trace logs and corrected the data in RDS after an 8-hour meeting. After a week of calls with RDS solution architects, snapshots kept from the old replicas confirmed that the lost data had been on the replicas, and innodb_flush_log_at_trx_commit on the master was changed from 2 to 1.","ko":"2019년 12월 금요일 저녁 Razorpay 의 API 애플리케이션이 RDS MySQL 데이터베이스에 접속하지 못해 5xx 오류를 냈다. 마스터 인스턴스가 Multi-AZ 장애 조치를 거쳤고, 약 110초 뒤 대기 인스턴스가 새 마스터가 되면서 애플리케이션이 스스로 살아났다. 곧바로 읽기 복제본 다섯 대 모두에서 duplicate-key 오류로 복제가 깨졌다. 팀은 오래된 데이터를 보여 주지 않으려고 복제본 트래픽을 모두 마스터로 옮겼고, 그 때문에 마스터의 CPU 부하가 50% 늘었다. 복제본을 다시 만드는 데 필요한 스냅샷은 장애 조치로 교체된 새 장비의 첫 스냅샷이라 전체 스냅샷이 됐다. 이 스냅샷에 여섯 시간이 걸렸다. 그 뒤에야 복제본을 만들고 트래픽을 되돌렸다.\n\n다음 날 팀은 데이터베이스와 애플리케이션 트레이스 로그를 맞춰 보다가 트레이스 로그에만 있는 레코드를 찾았다. 바이너리 로그와 대조하자 RDS 에서 5초 분량의 데이터가 사라져 있었다. 핀테크 회사에서 거래 데이터를 잃는 것은 돈과 고객 신뢰를 잃는 일이었다. 팀은 바이너리 로그에서 정확한 쿼리를 찾고 애플리케이션 트레이스 로그로 사건 순서를 따라가 8시간 회의 끝에 RDS 의 데이터를 바로잡았다.\n\n팀은 그 뒤 일주일 동안 RDS 솔루션 아키텍트들과 수십 번 통화하며 원인을 하나씩 맞춰 나갔다. Multi-AZ 구성에서 대기 인스턴스의 MySQL 은 꺼져 있고, 복제는 두 EBS 사이에서 동기식으로 일어난다. Razorpay 는 innodb_flush_log_at_trx_commit 을 RDS 기본값이자 완전한 ACID 준수에 필요한 1 이 아니라 2 로 두고 있었다. 2 이면 로그는 커밋마다 쓰지만 디스크에는 1초마다 플러시한다. 그래서 마스터에서 커밋된 데이터가 주 EBS 에 플러시되지 않았고, 대기 인스턴스는 이 데이터를 받지 못했다. 반면 sync_binlog 는 올바르게 1 로 설정돼 있어 데이터가 바이너리 로그에는 바로 기록됐다. 복제본은 이 바이너리 로그를 읽어 옛 마스터와 맞춰져 있었다. 새 마스터가 같은 auto-increment id 로 행을 다시 넣자 복제본에서 duplicate-key 오류가 나 복제가 멈췄다. 팀은 지우기 전에 남겨 둔 옛 복제본 스냅샷으로 잃은 데이터가 복제본에 있었다는 것을 증명했다. 그리고 마스터의 innodb_flush_log_at_trx_commit 을 2 에서 1 로 바꿨다."},"trigger":{"en":"The master instance of the API application's RDS MySQL database went through a Multi-AZ failover to its standby in another availability zone.","ko":"API 애플리케이션의 RDS MySQL 마스터 인스턴스가 Multi-AZ 장애 조치를 거쳐 다른 AZ 의 대기 인스턴스로 넘어갔다."},"severity":"unknown","symptoms":["high-error-rate","replication-lag","data-loss"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"published","detection":{"en":"An internal monitoring alert on unusually high 5xx errors, then alerts that replication had failed on all replicas. The data loss was found the next day by matching the database against the application trace logs.","ko":"내부 모니터링 도구가 5xx 오류가 평소보다 훨씬 많다고 알렸다. 복제 실패는 그 뒤 다른 알림으로 왔다. 데이터 손실은 다음 날 데이터베이스와 애플리케이션 트레이스 로그를 맞춰 보다가 찾았다."},"components":["database","database-replication","database-backup"],"root_cause":"config-change","remediation":[{"text":{"en":"Moved all read replica traffic to the master instance","ko":"오래된 데이터가 사용자에게 보이지 않도록 복제본으로 가던 트래픽을 모두 마스터 인스턴스로 옮겼다."}},{"text":{"en":"Recreated all five read replicas after a six-hour full snapshot and moved traffic back to them","ko":"새 장비의 전체 스냅샷이 끝나기를 여섯 시간 기다린 뒤 복제본 다섯 대를 다시 만들고 트래픽을 복제본으로 되돌렸다."}},{"text":{"en":"Recovered the missing queries from the binary logs and corrected the data stored in RDS","ko":"바이너리 로그에서 정확한 쿼리를 찾고 애플리케이션 트레이스 로그로 사건 순서를 따라가 8시간 회의 끝에 RDS 에 저장된 데이터를 바로잡았다."}},{"text":{"en":"Changed innodb_flush_log_at_trx_commit on the master from 2 to 1","ko":"마스터 인스턴스의 innodb_flush_log_at_trx_commit 값을 2 에서 1 로 바꿨다."}}],"action_items":[],"contributing":[],"recurrence_of":null,"symptoms_text":{"en":"On a Friday evening in December 2019, Razorpay's internal monitoring alerted on an unusually high number of 5xx errors. The SRE team found that the main application, called API, could not connect to its RDS MySQL database, but within about 110 seconds the application recovered by itself and the alerts stopped. The RDS logs showed that the master instance had gone through a Multi-AZ failover.\n\nRight after the application recovered, a second set of alerts reported that replication had failed on all five read replicas of the API master, each with a duplicate-key error. To keep stale or wrong data away from users, all replica traffic was moved to the master, whose CPU load rose by 50% because the heavier selects now ran there too.\n\nRecreating the replicas starts from a snapshot of the master. Incremental snapshots of the API database had taken about 15-20 minutes before, but this one was still running after about an hour. AWS support explained that the failover had replaced the old master with a new machine, so the first snapshot from it was a full snapshot; it took six hours.\n\nThe next day, AWS support said replication does not usually crash during a Multi-AZ failover. Comparing the database with the application trace logs, Razorpay found records that were in the trace logs but not in the database: about 5 seconds of data, just before the 5xx errors began, were missing from RDS.","ko":"2019년 12월 어느 금요일 저녁, 내부 모니터링 도구가 시스템에서 5xx 오류가 평소보다 훨씬 많이 난다고 알렸다. SRE 팀은 주요 애플리케이션인 API 가 RDS MySQL 데이터베이스에 접속하지 못한다는 것을 알아챘다. 원인을 파악하기도 전에 애플리케이션이 스스로 살아났고 알림이 멈췄다.\n\nRDS 로그를 보니 마스터 인스턴스가 Multi-AZ 장애 조치를 거쳤다. 마스터는 약 110초 만에 다시 올라왔고 애플리케이션은 손을 대지 않아도 동작했다.\n\n애플리케이션 오류가 멎은 직후 다른 알림이 왔다. 마스터에 딸린 읽기 복제본 다섯 대 모두에서 복제가 실패했고, 복제본마다 duplicate-key 오류가 떴다.\n\n복제본 트래픽을 마스터로 옮기자 무거운 SELECT 도 함께 넘어가 마스터의 CPU 부하가 50% 늘었다. 복제본을 다시 만들려고 스냅샷을 찍었는데, 평소 15~20분이면 끝나던 스냅샷이 한 시간이 지나도 끝나지 않았다. AWS 기술 지원에 따르면 장애 조치 때 옛 마스터를 새 장비로 바꿨기 때문에 그 장비의 첫 스냅샷이 전체 스냅샷이 됐다. 스냅샷이 끝나는 데 여섯 시간이 걸렸다.\n\n다음 날 RDS 기술 지원은 Multi-AZ 장애 조치에서 복제가 깨지는 일은 흔하지 않다고 했다. 데이터베이스와 애플리케이션 트레이스 로그를 맞춰 보니 트레이스 로그에는 있는데 데이터베이스에는 없는 레코드가 있었다. 바이너리 로그와 비교하자 RDS 데이터베이스에서 5초 분량의 데이터가 빠져 있었다. 그 5초 바로 뒤부터 애플리케이션 로그에 5xx 오류가 찍히기 시작했다."},"updated_at":"2026-11-14T12:33:58.390590Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0331","org_slug":"razorpay"}