{"id":"IL-0385","org":"GitHub","date":"2021-11-27","docs":[],"title":{"en":"GitHub MySQL read replicas hit a semaphore deadlock during a schema migration's table rename and kept crash-recovering","ko":"큰 MySQL 테이블의 스키마 마이그레이션 마지막 단계에서 읽기 복제본 다수가 semaphore deadlock 에 걸려 GitHub 핵심 서비스가 2시간 50분 저하됐다"},"domain":["database"],"impact":{"scope":"Core GitHub services were degraded: GitHub Actions, API Requests, Codespaces, Git Operations, Issues, GitHub Packages, GitHub Pages, Pull Requests and Webhooks; write operations stayed healthy","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":null,"related":[],"sources":[{"url":"https://github.blog/2021-12-01-github-availability-report-november-2021/","kind":"blog","archived":"https://web.archive.org/web/20240413005252/https://github.blog/2021-12-01-github-availability-report-november-2021/"}],"summary":{"en":"GitHub's MySQL clusters have a primary node for writes, several read replicas for production traffic, and several replicas that serve internal reads for backups and analytics. During the final rename step of a schema migration on a large table, a significant portion of the read replicas entered a semaphore deadlock, a failure mode GitHub had not seen before. Those replicas went into crash recovery, the remaining replicas took their load, and the cascade left too few active read replicas for production requests, which degraded core GitHub services for 2 hours and 50 minutes.\n\nTo add capacity, the team moved every healthy internal replica into the production path, but that was not enough. Replicas serving production traffic kept recovering and crashing again under load, so the team chose data integrity over availability and took production traffic off the broken replicas until each one had processed the table rename. Once the replicas recovered they were put back into production and capacity returned to normal. Writes stayed healthy and GitHub verified that no data was corrupted. GitHub paused schema migrations while it investigated the failure.","ko":"GitHub 은 스키마 마이그레이션을 자주 하고, 한 번에 몇 주가 걸리기도 한다. 마이그레이션의 마지막 단계는 갱신한 테이블을 rename 으로 제자리에 옮기는 일이다. 2021년 11월 27일 20:40 UTC 에 큰 MySQL 테이블의 마이그레이션이 이 단계에 이르자 읽기 복제본 상당수가 semaphore deadlock 에 걸렸다. GitHub 은 이것을 처음 겪는 장애 양상이라고 했다. GitHub 의 MySQL 클러스터는 쓰기를 받는 primary 노드 하나, 운영 트래픽을 받는 읽기 복제본 여러 대, 백업과 분석용 내부 읽기를 받는 복제본 몇 대로 이루어진다. deadlock 에 걸린 복제본은 crash-recovery 상태로 들어갔고, 정상 복제본에 부하가 몰리면서 연쇄로 번져 운영 요청을 감당할 활성 읽기 복제본이 모자라게 됐다. 그 결과 GitHub 의 핵심 서비스가 저하됐다.\n\n대응하는 동안 용량을 늘리려고 정상인 내부 복제본을 모두 운영 경로로 올렸지만 충분하지 않았다. 운영 트래픽을 받는 복제본은 잠시 회복했다가 부하로 다시 죽기를 되풀이했다. GitHub 은 사이트 가용성보다 데이터 무결성을 먼저 지키기로 하고, 망가진 복제본에서 운영 트래픽을 미리 빼 테이블 rename 을 끝까지 처리하게 했다. 복제본이 회복하자 운영에 다시 넣어 용량을 되찾았고, 장애는 2시간 50분 만에 끝났다. 쓰기 작업은 내내 정상이었고, GitHub 은 데이터 손상이 없었음을 확인했다."},"trigger":{"en":"The final rename step of a schema migration on a large MySQL table. Schema migrations are a common task at GitHub and often take weeks to complete.","ko":"큰 MySQL 테이블의 스키마 마이그레이션이 마지막 단계로 갱신한 테이블을 제자리로 옮기는 rename 을 실행했다. 이때 읽기 복제본 상당수가 semaphore deadlock 에 걸렸다."},"severity":"unknown","symptoms":["lock-contention","database-overloaded"],"timeline":{"detected_after":null,"resolved_after":"2h 50m"},"date_kind":"incident","detection":null,"components":["database","schema-migration"],"root_cause":"software-bug","remediation":[{"text":{"en":"Promoted every healthy internal replica into the production path to add read capacity","ko":"정상 상태인 내부용 복제본을 모두 운영 경로로 올려 용량을 늘렸다. 이것만으로는 완전히 복구되지 않았다."}},{"text":{"en":"Removed production traffic from the broken replicas until each had processed the table rename","ko":"crash-recovery 를 되풀이하는 복제본에서 운영 트래픽을 미리 빼고, 그 복제본이 테이블 rename 을 끝까지 처리할 때까지 기다렸다."}},{"text":{"en":"Moved the recovered replicas back into production to restore capacity","ko":"회복한 복제본을 운영에 다시 넣어 정상 운영에 필요한 용량을 되찾았다."}}],"action_items":[{"text":{"en":"Continue functional partitioning so migrations can run in canary mode on a single shard","ko":"클러스터를 기능별로 나누는 작업을 계속 우선해서, 마이그레이션을 샤드 하나에서 카나리 모드로 먼저 돌릴 수 있게 한다."}},{"text":{"en":"Update internal procedures to increase how much each cluster is over-provisioned","ko":"클러스터마다 여유 용량을 더 크게 잡도록 내부 절차를 고친다."}},{"text":{"en":"Pause schema migrations until it is known how to safeguard against this failure","ko":"이 문제를 막는 방법을 더 알게 될 때까지 스키마 마이그레이션을 멈춘다."}},{"text":{"en":"Keep investigating the specific failure scenario","ko":"이번 장애가 일어난 구체적인 조건을 계속 조사한다."}},{"text":{"en":"Test the migration tooling and classify ways to improve it for this kind of scenario","ko":"마이그레이션 도구를 시험하면서 이런 상황에서 도구를 고칠 점을 가려낸다."}}],"contributing":["no-spare-capacity","no-staged-rollout"],"recurrence_of":null,"symptoms_text":{"en":"On 27 November 2021 at 20:40 UTC, a significant part of GitHub's MySQL read replicas entered a semaphore deadlock while a schema migration on a large MySQL table ran its final step, the rename that moves the updated table into place. Core GitHub services, including GitHub Actions, API Requests, Codespaces, Git Operations, Issues, GitHub Packages, GitHub Pages, Pull Requests and Webhooks, became degraded.\n\nThe replicas that hit the deadlock went into crash recovery, which put more load on the healthy read replicas. Too few read replicas were left active to handle production requests. Replicas serving production traffic recovered from crash recovery for a while and then crashed again under the load. Write operations stayed healthy throughout.","ko":"2021년 11월 27일 20:40 UTC 부터 GitHub Actions, API 요청, Codespaces, Git 작업, Issues, GitHub Packages, GitHub Pages, Pull Requests, Webhooks 의 가용성이 떨어졌다. 큰 MySQL 테이블의 스키마 마이그레이션이 마지막 rename 단계를 실행하던 때였다.\n\nMySQL 읽기 복제본 상당수가 semaphore deadlock 에 걸린 뒤 crash-recovery 상태로 들어갔다. 이 복제본들이 빠지자 남은 정상 읽기 복제본에 부하가 몰렸다.\n\n부하가 연쇄로 번지면서 운영 요청을 받을 활성 읽기 복제본이 모자라게 됐다. 운영 트래픽을 받는 복제본은 crash-recovery 에서 잠시 살아났다가 부하 때문에 다시 죽기를 되풀이했다. 쓰기 작업은 내내 정상이었다.\n\n처음에는 용량을 늘리려고 정상 상태인 내부용 복제본을 모두 운영 경로로 올렸다. 그래도 완전히 복구되지는 않았다."},"updated_at":"2026-11-14T12:34:03.657259Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0385","org_slug":"github"}