{"id":"IL-0369","org":"Railway","date":"2025-12-09","docs":[],"title":{"en":"Railway's backend API went down when a column migration waited on locks and replicas could not get database connections","ko":"데이터베이스 마이그레이션이 잠금에 막히는 동안 Railway 백엔드 복제본이 헬스 체크에 실패해 대시보드와 API 가 멈췄다"},"domain":["database"],"impact":{"scope":"Railway backend API: dashboard access, CLI operations, GitHub-based deployment processing, login and API functionality; running deployments stayed online","users":"unknown","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":null,"related":[],"sources":[{"url":"https://blog.railway.com/p/incident-report-december-8-2025","kind":"blog","archived":"https://web.archive.org/web/20261023024713/https://blog.railway.com/p/incident-report-december-8-2025"}],"summary":{"en":"Railway's backend runs as several replicas connected to a shared PostgreSQL database, with PgBouncer pooling the connections. A migration that added a nullable column to a heavily used table of about 1 billion rows stalled because a long-running query on the same table held locks. While the migration waited, connection attempts piled up and PgBouncer exceeded the database's connection limit, so the replicas could not obtain connections, failed their health checks and were taken out of rotation, which took the backend API down. When the long-running query finished and released its locks, the migration applied at once, the replicas reconnected and passed their health checks, and the backend was serving traffic again at 15:26 UTC, eight minutes after the lock conflicts began. The report's title and byline give 8 December 2025, while its timeline places the incident on 9 December 2025.","ko":"Railway 는 공유 PostgreSQL 데이터베이스에 연결된 백엔드로 돌아가고, 연결 풀링은 PgBouncer 가 맡는다. 백엔드 요청은 여러 복제본이 나눠 받고, 복제본은 주기적인 헬스 체크를 통과해야 트래픽을 계속 받는다. 데이터베이스 연결을 얻지 못한 복제본은 비정상으로 표시되어 트래픽을 받지 않는다.\n\n12월 9일 Railway 는 약 10억 행이 있는 테이블에 nullable 열을 더하는 마이그레이션을 배포했다. 이 테이블에서 오래 도는 쿼리가 잠금을 잡고 있어 마이그레이션이 멈췄다. 그 사이 연결 시도가 쌓여 PgBouncer 가 데이터베이스의 연결 한도를 넘었고, 복제본들이 헬스 체크에 실패해 트래픽 대상에서 빠졌다. 오래 돌던 쿼리가 끝나 잠금을 풀자 마이그레이션이 곧바로 적용됐다. 복제본은 다시 연결하고 헬스 체크를 통과해 트래픽 대상으로 돌아왔다. 15:26 UTC 에 백엔드가 안정돼 다시 트래픽을 받았다."},"trigger":{"en":"Engineers merged a database schema change with a data migration at 15:03 UTC, and the migration began applying at 15:17 UTC. It added a nullable column to a heavily used table of about 1 billion rows.","ko":"엔지니어들이 15:03 UTC 에 데이터 마이그레이션이 들어간 데이터베이스 스키마 변경을 머지했다. 이 마이그레이션은 약 10억 행이 있고 많이 쓰는 테이블에 nullable 열을 더하는 것이었다."},"severity":"unknown","symptoms":["readiness-probe-failures","lock-contention","database-overloaded","service-unavailable"],"timeline":{"detected_after":null,"resolved_after":"8m"},"date_kind":"incident","detection":null,"components":["database","database-proxy","schema-migration","health-probe"],"root_cause":"deploy-rollout","remediation":[],"action_items":[{"text":{"en":"Increase database connections and reconfigure PgBouncer to work within those limits","ko":"데이터베이스 연결 수를 늘리고, PgBouncer 가 그 한도 안에서 동작하도록 다시 설정한다."}},{"text":{"en":"Fix the long-running query so it no longer causes row lock issues","ko":"오래 도는 쿼리를 고쳐 행 잠금 문제가 다시 나지 않게 한다."}}],"contributing":["probe-depends-on-dependency"],"recurrence_of":null,"symptoms_text":{"en":"At 15:18 UTC on 9 December 2025, Railway's backend replicas became unhealthy and stopped serving traffic. Dashboard access, CLI operations, logins, the API and the processing of GitHub-based deployments were disrupted, while running deployments stayed online.\n\nEach backend replica stays in rotation only while its periodic health check passes, and a replica that cannot get a database connection is marked unhealthy. Lock conflicts in the shared PostgreSQL database stalled a schema migration, connection attempts accumulated until PgBouncer went past the database's connection limit, and the replicas failed their health checks and were removed from rotation. All replicas recovered at 15:26 UTC, when the migration finished.","ko":"Railway 백엔드 API 가 트래픽을 받지 못해 대시보드 접속, CLI 작업, GitHub 기반 배포 처리, 로그인, API 가 멈췄다. 이미 돌고 있던 배포는 계속 온라인이었고, 이 시스템들을 쓰지 않은 사용자는 영향을 받지 않았다.\n\n백엔드 복제본은 주기적인 헬스 체크를 통과해야 트래픽을 받는다. 데이터베이스 연결을 얻지 못한 복제본은 비정상으로 표시되어 트래픽을 받지 않는다. 마이그레이션이 잠금에 막힌 사이 연결 시도가 쌓여 PgBouncer 가 데이터베이스의 연결 한도를 넘었고, 복제본이 헬스 체크에 실패해 트래픽 대상에서 빠졌다.\n\n12월 9일 15:17 UTC 에 데이터베이스 마이그레이션이 적용되기 시작했고, 15:18 UTC 에 데이터베이스의 잠금 충돌로 백엔드 복제본이 비정상이 됐다. 15:26 UTC 에 데이터 마이그레이션이 끝나자 모든 백엔드 복제본이 돌아왔다."},"updated_at":"2026-11-14T12:34:02.191393Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0369","org_slug":"railway"}