{"id":"IL-0215","org":"Twilio","date":"2013-07-18","docs":[],"title":{"en":"Twilio's billing Redis master lost all balances and went read-only, and auto-recharge charged customer cards repeatedly","ko":"Twilio 결제 시스템의 Redis 가 잔액 데이터를 잃고 읽기 전용이 되어 자동 충전 고객의 신용카드가 반복 결제됐다"},"domain":["database"],"impact":{"scope":"Twilio billing: payments did not update balances, auto-recharge charged cards repeatedly and some accounts were suspended; usage reports for all customers were delayed in reflecting prices while billing was offline; voice and SMS were not affected","users":"1.4% of Twilio customers (1.1% in the first wave, 0.3% in the recurrence)","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Losing the in-flight balances and the ability to write balances at the same time exposed a flaw in the auto-recharge system: it failed in a dangerous way, charging cards and suspending accounts instead of stopping.","ko":"Twilio 는 현재 잔액 데이터와 잔액을 갱신하는 능력을 동시에 잃자 자동 충전 시스템이 위험한 방향으로 실패했다고 봤다. 잔액이 없거나 쓸 수 없을 때 자동 충전 시스템은 계정을 정지하지 않고 카드를 결제하지 않는 쪽으로 멈춰야 했다."},"related":[],"sources":[{"url":"https://www.twilio.com/en-us/blog/company/communications/billing-incident-post-mortem-breakdown-analysis-and-root-cause-html","kind":"blog","archived":"https://web.archive.org/web/20261101130553/https://www.twilio.com/en-us/blog/company/communications/billing-incident-post-mortem-breakdown-analysis-and-root-cause-html"}],"summary":{"en":"Twilio's billing system keeps in-flight account balances in an in-memory Redis cluster with a single master and several slaves spread over data centers, and keeps an independent double-bookkeeping record of all balances in a separate relational datastore. When network connectivity was lost at 1:35 AM PDT, all slaves reconnected and requested full synchronization together, and by 2:39 AM PDT the load on the master's host made services that depend on it fail. On-call engineers saw the extreme load and restarted the redis process on the master, a misdiagnosis. On restart Redis read an incorrect configuration file: it tried to recover from a non-existent AOF file instead of the binary snapshot, dropped all balance data, and booted as a slave of itself, which put it in read-only mode.\n\nWith every balance at zero and no way to write balances, any billable usage, such as 1 cent for an SMS message or a phone call, made the billing system try to recharge the credit card of accounts with auto-recharge enabled, again and again. Monitoring reported the anomaly at 3:28 AM PDT and the billing system was shut down at 4:10 AM PDT. Suspended accounts were restored by 6:24 AM PDT, and the double-bookkeeping record was used to restore the lost balances. Billing was turned back on at 11:58 AM PDT, but the anomalies came back at 12:36 PM PDT for another 0.3% of customers, and billing was shut down again while suspended accounts were reactivated.\n\nRefunds for the erroneous charges started at 2:57 PM PDT on 18 July and were finished at 3:00 PM PDT on 19 July, and all affected accounts received a credit of 10% of their last 30 days of Twilio spend. The original Redis cluster was replaced and the incorrect configuration corrected. From 7:10 PM PDT on 19 July billing was activated progressively for groups of accounts, it was active for all customers at 8:14 PM PDT, and the all-clear was given at 9:15 PM PDT.","ko":"Twilio 결제 시스템은 현재 계정 잔액을 메모리 기반 Redis 클러스터에 둔다. 이 클러스터는 master 하나와 여러 데이터센터에 나눠 둔 slave 들로 이루어진다. 2013년 7월 18일 1:35 AM PDT 에 네트워크 연결이 끊겨 모든 redis-slave 가 동시에 master 와 끊겼다가 다시 붙으며 전체 동기화를 요청했고, master 는 극심한 부하에 빠졌다. 2:39 AM PDT 에 redis-master 에 기대는 서비스들이 실패하기 시작했고, 2:42 AM PDT 에 당번 엔지니어는 부하를 보고 redis-master 를 재시작했다.\n\n재시작한 redis-master 는 잘못된 설정 파일을 읽어 바이너리 스냅샷 대신 없는 AOF 파일로 복구하려다 실패했고, 모든 잔액 데이터를 잃었다. 잘못된 설정은 redis-master 를 자기 자신의 slave 로 띄워 읽기 전용으로 만들었다. 잔액이 0 이고 갱신할 수 없으니 자동 충전이 켜진 계정은 과금 거래가 생길 때마다 신용카드 충전을 시도했고, 같은 카드가 반복해서 결제됐다. 반복 결제로 비활성화된 카드에 충전을 시도한 계정은 정지됐다.\n\n3:28 AM PDT 에 모니터링이 이상을 찾아 고객의 1.1% 가 영향을 받은 것을 알았고, 4:10 AM PDT 에 결제 시스템을 내렸다. 6:24 AM PDT 에 정지된 계정을 모두 되살렸고, 별도 관계형 데이터 저장소에 따로 적어 둔 잔액 기록으로 잔액을 복원했다. 11:58 AM PDT 에 결제 시스템을 다시 켰지만 12:36 PM PDT 에 같은 이상이 다시 나타나 고객 0.3% 가 더 영향을 받았고, 다시 내렸다. 2:57 PM PDT 부터 잘못된 결제를 환불해 7월 19일 3:00 PM PDT 에 마쳤고, 영향받은 계정에 최근 30일 사용액의 10% 를 크레딧으로 줬다. 문제를 일으킨 Redis 클러스터는 교체했고 잘못된 설정을 고쳤다. 7:10 PM PDT 부터 결제 시스템을 계정 묶음마다 차례로 켜 8:14 PM PDT 에 모든 고객에게 켰고, 9:15 PM PDT 에 모든 시스템이 복구됐다. 영향받은 고객은 모두 1.4% 였다."},"trigger":{"en":"At 1:35 AM PDT on 18 July a loss of network connectivity disconnected all billing redis-slaves from the redis-master at once, and all of them requested a full synchronization at the same time. On-call engineers then restarted the overloaded redis-master.","ko":"7월 18일 1:35 AM PDT 에 네트워크 연결이 끊겨 결제용 redis-slave 전부가 동시에 redis-master 와 끊겼고, 다시 붙으면서 한꺼번에 전체 동기화를 요청했다. 부하가 높아진 redis-master 를 당번 엔지니어가 재시작했다."},"severity":"unknown","symptoms":["database-overloaded","data-loss","writes-rejected"],"timeline":{"detected_after":"1h 7m","resolved_after":"43h 40m"},"date_kind":"incident","detection":{"en":"At 2:42 AM PDT monitoring alerted the on-call engineers to a failure in the Redis cluster. At 3:28 AM PDT the billing system monitoring reported the anomalous charges, and at 12:36 PM PDT monitoring detected the recurrence.","ko":"2:42 AM PDT 에 모니터링 시스템이 Redis 클러스터 장애를 당번 엔지니어링 팀에 알렸다. 잘못된 결제는 3:28 AM PDT 에 결제 시스템 모니터링이 이상 동작을 보고해 알았고, 12:36 PM PDT 에는 모니터링이 같은 이상의 재발을 찾았다."},"components":["cache","database-replication"],"root_cause":"config-change","remediation":[{"text":{"en":"Restarted the redis-master to relieve the high load, which loaded a wrong configuration","ko":"2:42 AM PDT 에 당번 엔지니어가 높은 부하를 해결하려고 redis-master 를 재시작했다."}},{"text":{"en":"Took the billing system offline to stop further credit card charges","ko":"4:10 AM PDT 에 신용카드 결제가 더 일어나지 않게 결제 시스템을 내렸다."}},{"text":{"en":"Restored service to all suspended accounts","ko":"6:24 AM PDT 에 정지된 계정을 모두 되살렸고, 결제 시스템은 원인을 조사하는 동안 꺼 둔 채로 뒀다."}},{"text":{"en":"Restored the lost account balances from the independent double-bookkeeping record in the relational datastore","ko":"별도 관계형 데이터 저장소에 따로 적어 둔 잔액 기록으로 redis-master 에서 잃은 계정 잔액을 복원했다."}},{"text":{"en":"Took billing offline again after the recurrence and reactivated the newly suspended accounts","ko":"12:36 PM PDT 에 같은 이상이 다시 나타나자 결제 시스템을 다시 내리고 정지된 계정을 바로 되살렸다."}},{"text":{"en":"Refunded all erroneous credit card charges","ko":"2:57 PM PDT 부터 잘못된 신용카드 결제를 환불해 7월 19일 3:00 PM PDT 에 모두 마쳤다."}},{"text":{"en":"Gave all affected accounts a credit of 10% of their last 30 days of spend","ko":"7월 19일 3:30 PM PDT 에 영향받은 모든 계정에 최근 30일 Twilio 사용액의 10% 를 크레딧으로 줬다."}},{"text":{"en":"Replaced the original Redis cluster and corrected the redis-master configuration","ko":"장애를 일으킨 원래 Redis 클러스터를 교체했고, redis-master 의 잘못된 설정을 찾아 고쳤다."}},{"text":{"en":"Activated the billing system progressively for groups of customer accounts","ko":"7월 19일 7:10 PM PDT 부터 고객 계정 묶음마다 결제 시스템을 차례로 켰다."}}],"action_items":[{"text":{"en":"Disable Redis restarts on the redis-master and recover the master by pivoting a slave instead","ko":"redis-master 의 Redis 재시작을 막고, 앞으로 redis-master 복구는 slave 를 승격해 처리한다."}},{"text":{"en":"Add fail-safes so the billing system neither suspends accounts nor charges cards when balances are missing or cannot be written","ko":"결제 잔액이 없거나 쓸 수 없으면 계정을 정지하거나 신용카드를 결제하지 않도록 안전장치를 넣는다."}},{"text":{"en":"Validate the billing system against the double-bookkeeping databases in real time","ko":"결제 시스템이 잔액을 따로 적어 두는 데이터베이스와 실시간으로 대조하게 바꾼다."}}],"contributing":["retry-storm","manual-action-worsened","unsafe-failure-mode"],"recurrence_of":null,"symptoms_text":{"en":"At 2:39 AM PDT (9:39 UTC) on 18 July 2013, services that relied on the redis-master of Twilio's billing system began to fail because the load on its host had become extreme. At 2:42 AM PDT monitoring alerted the on-call engineers to a failure in the Redis cluster.\n\nThe load had built up for about an hour. At 1:35 AM PDT network connectivity was lost between all billing redis-slaves and the redis-master, and every slave reconnected and requested a full synchronization with the master at the same moment. Serving those full sync requests degraded the master, and the slaves timed out on their connections to it.\n\nSeeing the extreme load, the on-call engineers misdiagnosed the redis process on the redis-master as needing a restart. On restart the redis-master read an incorrect configuration file, dropped all balance data and booted as a slave of itself in read-only mode, so the billing system could not update account balances.\n\nAt 3:28 AM PDT, after the redis-master had been restarted, the billing system monitoring reported anomalous activity: erroneous credit card charges and, in some cases, account suspensions, affecting 1.1% of all Twilio customers. Credit card payments did not update account balances, auto-recharge tried to charge the same card many times, and some accounts were suspended when a recharge hit a card that the repeated billing had deactivated. Usage reports for all customers were delayed in reflecting the prices of billable items while billing was offline. Voice and SMS kept working, but prices were not calculated for the calls and messages made during the incident.\n\nThe billing system was brought back online at 11:58 AM PDT after the balances had been restored, and at 12:36 PM PDT monitoring detected the same billing anomalies again, affecting another 0.3% of customers, so billing was taken offline a second time.","ko":"2013년 7월 18일 2:42 AM PDT 에 모니터링 시스템이 결제용 Redis 클러스터의 장애를 당번 엔지니어에게 알렸다. redis-master 호스트의 부하가 극도로 높았고, redis-master 에 기대는 서비스들이 실패하고 있었다.\n\n1:35 AM PDT 에 네트워크 연결이 끊기며 모든 redis-slave 가 동시에 master 와 끊겼다. redis-slave 들이 한꺼번에 다시 붙어 전체 동기화를 요청하자 master 의 성능이 떨어지고 redis-slave 에서 redis-master 로 가는 요청이 타임아웃됐다.\n\n당번 엔지니어는 극심한 부하를 보고 redis-master 의 redis 프로세스를 재시작해야 한다고 잘못 판단했다. 재시작한 redis-master 는 모든 잔액 데이터를 잃었고 읽기 전용 상태가 되어 잔액을 갱신할 수 없었다.\n\n3:28 AM PDT 에 결제 시스템 모니터링이 이상 동작을 보고했다. 잘못된 신용카드 결제와 일부 계정 정지가 일어났고, Twilio 전체 고객의 1.1% 가 영향을 받았다. 고객이 신용카드로 결제해도 계정 잔액에 반영되지 않았다. 잔액이 모두 0 이라 SMS 한 건이나 전화 한 통처럼 1센트짜리 사용도 자동 충전을 일으켰다. 충전해도 잔액이 갱신되지 않아 같은 카드에 결제가 거듭 시도됐다. 반복 결제로 비활성화된 카드에 충전을 시도한 일부 계정은 정지됐다.\n\n결제 시스템이 꺼져 있는 동안 모든 고객의 사용량 보고서에 과금 항목의 가격이 늦게 반영됐다. 음성과 SMS 서비스는 영향을 받지 않았지만 그동안의 통화와 메시지 가격은 계산되지 않았다.\n\n11:58 AM PDT 에 결제 시스템을 다시 켜자 12:36 PM PDT 에 모니터링이 같은 이상을 다시 찾았고, 고객의 0.3% 가 더 영향을 받았다. 결제 시스템은 다시 내려졌다."},"updated_at":"2026-11-14T05:53:42.454098Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0215","org_slug":"twilio"}