{"id":"IL-0272","org":"Monzo","date":"2019-07-29","docs":[],"title":{"en":"New Cassandra nodes served reads for data they did not hold, so Monzo records looked missing and payments failed","ko":"Cassandra 에 새로 넣은 서버가 데이터 없이 읽기와 쓰기를 맡아 데이터가 사라진 것처럼 보이면서 Monzo 의 로그인과 결제가 실패했다"},"domain":["database"],"impact":{"scope":"Monzo app logins, payments, ATM withdrawals, balances and transactions shown in the app, in-app chat and phone support; a small percentage of card transactions failed","users":"Monzo customers; most were working again at 15:08 and all at 23:00","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Monzo had gaps in its knowledge of rarer Cassandra operations such as adding servers. The scale-up test with one server could not show the problem, because quorum still came from the original replicas, so it did not match adding six servers in production. The lack of metrics pointing at Cassandra delayed the response, and a single shared cluster both let one change have a far-reaching impact and made the failing part harder to find.","ko":"Monzo 는 Cassandra 운영 지식에 빈틈이 있었다. 롤링 재시작이나 서버 업그레이드는 자주 했지만 서버 추가는 드물었다. 시험 환경에서는 운영과 같은 규모로 시험하지 않아 여섯 대 대신 한 대로 시험했고, 쿼럼을 고려하지 못했다. Cassandra 가 원인임을 보여 주는 지표가 없어 대응이 늦었다. 클러스터 하나로 모든 서비스를 돌리면 변경 하나의 영향이 넓고 장애 원인을 짚기도 어렵다. 이번에는 첫 알림부터 Cassandra 를 원인으로 짚기까지 거의 한 시간이 걸렸다."},"related":[],"sources":[{"url":"https://monzo.com/blog/2019/09/08/why-monzo-wasnt-working-on-july-29th/","kind":"blog","archived":"https://web.archive.org/web/20261025113247/https://monzo.com/blog/2019/09/08/why-monzo-wasnt-working-on-july-29th"}],"summary":{"en":"Monzo stores its data in a single Cassandra cluster of 21 servers shared by all its services, each with its own keyspace. Every piece of data is replicated to three servers chosen by its partition key, and all reads and writes use quorum, so two of the three servers must agree. The cluster had last been scaled up in October 2018, and growth in customers and microservices had brought it closer to its limits at peak load than Monzo wanted, so six servers were to be added. After the October 2018 scale-up Monzo had set auto_bootstrap to false in production, so that a failed server could be replaced from backups instead of streaming its data from the other servers. Monzo believed the flag only controlled data streaming and that new servers would join inactive until data was streamed to them one by one. The flag also decides whether a new server joins active: with it set to false, the six servers agreed on the partitions they owned and began serving queries without holding any of that data. The procedure had been tested with one new server in each of two clusters, where quorum still came from the two original replicas; with six servers in production, two or three replicas of some partitions moved to the new nodes, so some data appeared to be missing.\n\nThe first symptoms were failing card transactions and broken support tooling, and the deployment tooling failed too, so the Mastercard fix went out at 13:39 through a planned fallback mechanism. From 13:46 the team traced the internal edge's 404s to a missing key in the configuration service, then to Cassandra, and at 14:13 confirmed that a new server had answered a query. At 14:18 engineers began decommissioning the new servers one by one, each taking about 8 to 10 minutes. The 404s dropped as soon as the first was removed at 14:28, and when the last left at 15:08 Monzo worked normally again for most customers. Until 23:00 the team reconciled the data written while the new servers were serving reads and writes, by replaying externally stored events and running internal consistency checks; at 23:00 all customers could use Monzo again and the data was fully recovered. It took almost an hour from the first alert to see that Cassandra was at fault.","ko":"Monzo 는 데이터를 Cassandra 클러스터 하나에 저장한다. 서버 21대로 이루어져 있고, 모든 데이터는 21대 가운데 세 대에 복제되며, 파티션 키가 어느 세 대가 그 데이터를 맡을지 정한다. 모든 읽기와 쓰기는 쿼럼으로 해서 세 대 가운데 적어도 두 대가 값을 확인해야 한다. 사용자와 마이크로서비스가 늘어 최대 부하 때 클러스터가 한계에 가까워지자 Monzo 는 클러스터를 키우기로 했다.\n\n7월 29일 13:10 BST 에 새 서버 여섯 대를 넣었다. 팀은 새 서버가 클러스터에 들어온 뒤 데이터를 스트리밍할 때까지 비활성으로 머문다고 믿었다. 2018년 10월 확장 뒤 운영 환경의 auto_bootstrap 을 false 로 바꿔 두었는데, 서버를 잃어 교체할 때 다른 서버에서 다시 만들지 않고 백업에서 복원하려는 것이었다. 그런데 이 플래그는 데이터 스트리밍뿐 아니라 새 서버가 활성으로 들어오는지도 정한다. 새 서버들은 맡을 파티션을 정하자마자 데이터 없이 그 파티션을 완전히 맡아 질의를 처리했다. 그래서 일부 데이터가 사라진 것처럼 보였다. 시험 환경에서는 서버 한 대로만 시험했고, 한 대로는 나머지 두 대가 쿼럼을 맞춰 주므로 문제가 드러나지 않았다. 여섯 대를 넣자 일부 데이터의 담당 서버 세 대 가운데 두세 대가 새 서버로 바뀌었다.\n\n그 결과 카드 결제 일부가 실패했다. 설정 서비스에서 키를 찾지 못한 internal edge 가 404 를 내 고객 응대 도구와 배포 도구가 멈췄다. 고객은 로그인, 결제, 잔액 확인을 제대로 하지 못했다. 팀은 Mastercard 서비스의 코드 오류를 고친 뒤 설정 서비스와 Cassandra 를 따라가 14:13 에 새 서버가 응답하고 있다는 것을 확인했다. 14:18 부터 새 서버를 한 대씩 빼 데이터 담당을 원래 서버로 돌렸다. 한 대마다 8~10분이 걸렸다. 14:28 에 첫 서버를 빼자 404 가 곧바로 줄었고, 15:08 에 마지막 서버를 빼자 대부분의 고객에게 Monzo 가 정상으로 돌아왔다. 새 서버가 질의를 처리하는 동안 쓰인 데이터는 외부에 저장해 둔 이벤트를 재생하고 내부 정합성 확인 작업을 돌려 23:00 까지 바로잡았고, 데이터는 모두 복구됐다."},"trigger":{"en":"At 13:10 BST Monzo added six new servers to its 21-server production Cassandra cluster to add capacity, with auto_bootstrap set to false.","ko":"2019년 7월 29일 13:10 BST 에 Cassandra 클러스터에 새 서버 여섯 대를 넣었다. 운영 환경의 auto_bootstrap 플래그는 false 였다."},"severity":"unknown","symptoms":["inconsistent-reads","high-error-rate"],"timeline":{"detected_after":"4m","resolved_after":"9h 50m"},"date_kind":"incident","detection":{"en":"At 13:14 automated alerts reported a small percentage of Mastercard card transactions failing, and Customer Operations reported that its customer communication tool was not working; an incident was declared at 13:15.","ko":"13:14 BST 에 Mastercard 카드 거래 일부가 실패한다는 자동 알림이 울렸고, 같은 시각 고객 응대 팀이 고객 소통 도구가 동작하지 않는다고 알려 왔다."},"components":["database"],"root_cause":"config-change","remediation":[{"text":{"en":"Deployed a fix for an error-handling bug in a Mastercard service through a planned fallback mechanism, because the deployment tooling was failing","ko":"계획해 둔 대체 배포 방법으로 Mastercard 서비스의 수정을 배포했다."}},{"text":{"en":"Decommissioned the six new Cassandra servers one by one so data ownership returned to the original servers","ko":"새 Cassandra 서버를 한 대씩 빼서 데이터 담당을 원래 서버로 안전하게 되돌렸다."}},{"text":{"en":"Reconciled the data written while the new servers were serving reads and writes, by replaying externally stored events and running consistency checks","ko":"새 서버 여섯 대가 읽기와 쓰기를 처리하는 동안 쓰인 데이터를, 외부에 저장한 이벤트를 재생하고 내부 정합성 확인 작업을 돌려 바로잡았다."}}],"action_items":[{"text":{"en":"Fix the use of auto_bootstrap","ko":"auto_bootstrap 설정을 바로잡았다."}},{"text":{"en":"Review and document the decisions behind the other Cassandra settings and write more extensive runbooks for operations such as scale-ups and restarts","ko":"Cassandra 의 다른 설정을 두고 내린 결정을 모두 검토하고 문서로 남겨, 확장이나 재시작 같은 작업의 런북을 늘렸다."}},{"text":{"en":"Expose more Cassandra metrics and consider alerting on strong shifts in metrics such as 'row not found'","ko":"더 많은 지표를 드러내고, 'row not found' 같은 지표가 크게 바뀌면 알림을 보내는 방안을 검토한다."}},{"text":{"en":"Split the single Cassandra cluster into several smaller clusters","ko":"장기적으로 큰 Cassandra 클러스터 하나를 작은 클러스터 여러 개로 나눈다."}}],"contributing":["assumption-unverified","staging-not-representative","monitoring-blind-spot","blast-radius-too-wide","circular-dependency"],"recurrence_of":null,"symptoms_text":{"en":"On 29 July 2019 at 13:14 BST, four minutes after six new servers were added to Monzo's Cassandra cluster at 13:10, automated alerts showed a small percentage of Mastercard card transactions failing, and the tool Customer Operations uses to talk to customers through in-app chat stopped working. Customers could not log in, send or receive payments or withdraw cash, and saw inaccurate balances and transactions.\n\nAt 13:29 Monzo's internal edge, the service that checks staff access and forwards requests to internal tools such as customer operations tooling and the deployment pipeline, began returning HTTP 404 responses. At 13:32 customers were being logged out of the Android and iOS apps, and at 13:33 the deployment tooling was failing as well. The internal edge was not routing internal traffic because a lookup in Monzo's configuration service, a key-value microservice that stores its data in Cassandra, returned a 404 (not found) for the key the internal edge used. Querying Cassandra directly confirmed that the key was missing, while other keys were still there. Transactions that should have existed could not be found, so account balances appeared wrong.\n\nThe evidence looked contradictory. The database and client metrics showed no increase in errors, no change in latency and normal read and write rates right after the scale-up, and the configuration service metrics showed successful reads and writes while it failed to return the key. The engineer who started the scale-up joined at 13:24, and the scale-up was ruled out as a cause because everything looked healthy. The payments team found a small error-handling bug in a Mastercard service and took it for the cause. Only at 14:13 did a query show that the answer came from one of the new servers, which the team had believed impossible.","ko":"2019년 7월 29일 13:14 BST 에 Mastercard 카드 거래 일부가 실패한다는 자동 알림이 울렸다. 같은 시각 고객 응대 팀이 고객과 소통하는 도구가 제대로 동작하지 않아 앱 채팅으로 고객을 도울 수 없다고 알려 왔다.\n\n고객은 앱에 로그인하지 못하거나, 결제와 송금을 주고받지 못하거나, ATM 에서 돈을 찾지 못했다. 앱에 잔액과 거래 내역이 틀리게 보였다. 있어야 할 거래를 찾지 못해 잔액이 잘못 나온 것이다. 13:32 에는 Android 와 iOS 앱에서 일부 고객이 로그아웃된다는 신고가 들어왔다.\n\n13:29 에 Monzo 직원이 내부 서비스에 접근할 때 쓰는 internal edge 가 HTTP 404 응답을 내는 것이 보였다. 13:33 에 Mastercard 수정을 배포하려 했지만 배포 도구도 실패하고 있었다.\n\n데이터베이스를 바꾼 엔지니어가 13:24 에 조사에 들어와 Cassandra 확장이 원인일 수 있는지 논의했지만, 오류도 늘지 않았고 읽기·쓰기 비율도 그대로여서 그 가능성을 뺐다. 결제 팀은 Mastercard 서비스의 작은 코드 오류를 찾아 원인이라고 보고 고쳤다. 13:39 에 수정을 배포하자 카드 거래 성공이 곧바로 나아졌지만 완전히 정상이 되지는 않았다.\n\ninternal edge 는 설정 서비스로 요청이 사설 네트워크 대역에서 왔는지 확인한다. 13:48 에 설정 서비스에 직접 물어보니 그 키에 404 를 냈다. 그런데 지표에는 설정 서비스의 읽기와 쓰기가 성공하고 있었고, 다른 키들은 그대로 있었다. 증거가 서로 맞지 않았다.\n\n14:00 에 설정 서비스를 거치지 않고 Cassandra 에 직접 물어 internal edge 가 쓰는 키가 Cassandra 에 없다는 것을 확인했다. 14:13 에 Cassandra 에 질의한 응답이 새 서버 가운데 하나에서 온다는 것을 확인했다. 불가능하다고 여긴 일이 실제로 일어나 있었다."},"updated_at":"2026-11-14T12:33:52.367171Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0272","org_slug":"monzo"}