{"id":"IL-0286","org":"Honeycomb","date":"2025-12-05","docs":[],"title":{"en":"An exercise cleanup destroyed Honeycomb's Kafka brokers, leaving partitions leaderless and ingest crash-looping","ko":"Honeycomb 운영 Kafka 클러스터에서 브로커가 한꺼번에 사라져 토픽 파티션 여럿이 리더를 잃고 일부는 되살릴 수 없게 망가졌다"},"domain":["database","aws","observability"],"impact":{"scope":"Ingest failed for teams and environments whose partitions were all unavailable; one Retriever partition, about 2.5% of the cluster, lost all its data; older data on truncated partitions could not be queried for a while; SLO processing saw ~18h of late data; Activity Log data from 5 December ~6 p.m. ET to 9 December ~6 p.m. ET was lost","users":"the Honeycomb teams and environments assigned to the damaged partitions","region":"unknown"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Many elements of this outage were already known: similar outages, workaround plans, RFCs, migrations and procedures for running such exercises safely existed, but the documents were unknown outside their authors or hard to find. Flexible, locally controlled work is easy to defer again and again in favour of urgent work, and Honeycomb wants to notice when deferring becomes a reflex. Many key interventions came from people who no longer owned the components, which shows drift between team boundaries and the coupling inside the system. The pre-production clusters were too small to reproduce the flaw, and tests on a cluster closer to production would have made catching it much more likely. Coordination and public communication were also hard: senior engineers tended to take the lead automatically, and customers said the updates gave too much detail about minor beta features.","ko":"Honeycomb 은 \"다음에는 Kafka 클러스터를 없애지 말자\" 는 설명에서 멈추지 않고 더 넓은 영향을 봐야 한다고 밝혔다. 사전 운영 클러스터는 노드가 적어 구조상 이 문제를 재현하기 어려웠고, 운영과 더 닮은 클러스터에서 시험했다면 잡을 가능성이 훨씬 높았다. 이번 장애에 얽힌 요소 상당수는 이미 알려져 있었고 우회 계획, RFC, 안전하게 훈련하는 절차를 적은 문서도 있었지만, 쓴 사람 말고는 그런 문서가 있는지 몰랐거나 검색으로 찾을 수 없었다. 마감이 멀고 범위가 불확실한 내부 작업은 급한 작업에 밀려 거듭 미뤄지기 쉽다. 핵심 개입 상당수는 이제 그 구성 요소를 맡지 않는 사람들에게서 나왔고, 이는 팀 구성과 기술 구성 사이에 틈이 벌어졌다는 뜻이다. 에스컬레이션을 더 빨리 했어야 했다는 의견과 대응을 지속 가능하게 해야 한다는 의견이 함께 나왔다. 선임 엔지니어들이 들어오자마자 저절로 지휘를 잡는 문제와, 베타 기능의 손실을 너무 자세히 알린 대외 소통도 과제로 남았다."},"related":[],"sources":[{"url":"https://honeycomb.io/blog/incident-report-exercises-cleanups-and-evacuations","kind":"blog","archived":"https://web.archive.org/web/20261102123803/https://www.honeycomb.io/blog/incident-report-exercises-cleanups-and-evacuations"}],"summary":{"en":"Honeycomb runs disaster recovery exercises every year, in production too. Each Honeycomb instance runs in one region over at least three availability zones, and in production each Kafka topic partition is replicated to one broker per zone. When a zone fails, an AWS auto-scaling group boots replacement brokers in working zones, which then copy data from the in-sync replicas. In the cleanup after the 5 December exercise, the brokers that were killed included older in-sync ones, so some partitions were left with no broker at all. The EU pre-production clusters have fewer nodes: there one broker is killed in the test and one in the cleanup, which makes it far less likely that a partition loses all its brokers, so four pre-production runs had not shown the flaw. Retriever stores the last Kafka offset it has seen and refuses to process data when offsets reset, so a dirty leader election could break Retriever ingestion for good. Kafka here also uses Confluent tiered storage, which keeps fresh data on local disks and older data on S3.\n\nAbout an hour in, responders had found the affected customers. The impacted partitions were marked read-only on the producer side, which stopped Shepherd's crash loop, and reassignments brought customers back. The Kafka autobalancer was turned off and a manual reassignment ran overnight, local retention was lowered around midnight PT, and on Saturday, with disk running out, responders planned to shut ingest at the ALB at 95% usage, cancelled the main reassignments and repaired the broken _confluent-tier-state and __consumer_offsets partitions, which also unblocked SLO processing. Ingest was turned off at 7 a.m. PT, and while new brokers, Retrievers and partitions were being prepared, tiered storage was turned off, disk space came back at once and ingest was reopened, ending the customer impact. Fully damaged Retriever partitions were truncated and left read-only, so older data could not be queried for a while but was not lost.\n\nIn the second week Honeycomb extended the binlog retention of its MySQL read-only replica, because Activity Log could no longer publish events to Kafka, and patched Retriever so old data could be queried despite broken offsets. An attempt to turn tiered storage back on failed. On Wednesday a topic delete timed out and left the cluster unable to do any administrative work beyond listing topics, so the team split into one lane trying low-risk fixes and ZooKeeper surgery and one planning a full evacuation to a new Kafka cluster, with five scenarios depending on what would be ready. On Friday the Storage team made a one-time offset reset work for Retriever, and the platform team had made good progress on feature flags to switch readers and writers and had made it possible to boot a second Kafka cluster. After a dress rehearsal in an EU pre-production cluster on Monday, which took four hours, and a fix for NLB domain names that did not fit AWS limits, the migration on Tuesday took two hours with at most one hour of delayed signal. An attempt to stretch MySQL binlog retention with a stalled replica did not work, and Activity Log data from 5 December at ~6 p.m. ET to 9 December at ~6 p.m. ET was lost.","ko":"Honeycomb 은 해마다 운영 환경을 포함한 여러 환경에서 재해 복구 시나리오를 돌린다. 12월 5일 가용 영역 장애 훈련을 마친 뒤, 장애 동안 새 가용 영역에 몰린 워크로드를 다시 고르게 나누는 정리 단계를 런북대로 진행하면서 Kafka 브로커를 일부러 없앴다. 이때 동기화가 끝나 있던 기존 브로커까지 함께 없어져, 일부 토픽 파티션은 그 파티션을 가진 브로커가 하나도 남지 않았다. 브로커가 적은 EU 사전 운영 클러스터에서는 구조상 이 상황이 거의 일어나지 않아 네 번의 시험에서 문제가 드러나지 않았다. 브로커를 모두 잃은 파티션은 dirty 리더 선출을 거쳐야 하고, 이 선출은 offset 을 되돌릴 수 있다. offset 이 되돌아가면 쿼리 엔진 Retriever 가 데이터 처리를 거부한다. Retriever 는 offset 으로 어떤 이벤트까지 처리했는지를 엄격히 추적하기 때문이다.\n\n첫날 대응은 파티션을 옮길 팀과 환경을 찾고, offset 과 데이터를 잃지 않고 파티션을 살리는 두 갈래로 나뉘었다. 망가진 파티션을 프로듀서 쪽에서 읽기 전용으로 돌려 수집 서비스 Shepherd 의 크래시 루프를 멈추고 재배치를 돌렸다. 밤새 돌린 재배치는 진척이 없었고 디스크는 계속 찼다. 토요일 아침 7시 PT 에 여유 공간이 5.07% 일 때 수집을 껐다. tiered storage 를 끄자 디스크 공간이 바로 돌아왔고, 수집을 다시 열면서 고객 영향이 끝났다. 완전히 망가진 Retriever 파티션은 잘라 내고 읽기 전용으로 두었다.\n\n다음 주 수요일 Activity Log 토픽을 지우려다 삭제가 타임아웃 나면서 클러스터가 토픽 목록 말고는 아무 관리 작업도 못 하게 됐다. 팀은 클러스터를 조심스럽게 살리는 길과 새 클러스터로 완전히 옮기는 길을 나눠 진행했다. 금요일에 Storage 팀이 Retriever 의 offset 초기화 장치를 완성했다. 월요일에 EU 사전 운영 클러스터에서 전체 리허설을 네 시간 안에 마쳤고, 화요일에 새 클러스터로 두 시간 만에 옮겼다. 영업일로 5일이 안 되는 기간이었다. Activity Log 는 12월 5일 오후 6시 무렵 ET 부터 12월 9일 오후 6시 무렵 ET 까지의 데이터를 잃었다."},"trigger":{"en":"During the cleanup that rebalances workloads after a successful zonal failover exercise, Honeycomb followed its runbook and destroyed Kafka brokers on purpose. Older, in-sync brokers were killed by accident, so some topic partitions lost every broker that held them.","ko":"12월 5일 운영 환경에서 가용 영역 장애 훈련을 마친 뒤, 워크로드 균형을 되돌리는 정리 단계를 런북대로 진행하다 Kafka 브로커를 일부러 없앴다. 이때 동기화가 끝나 있던 기존 브로커까지 함께 없어졌다."},"severity":"unknown","symptoms":["crash-loop","data-loss","node-disk-full","monitoring-down"],"timeline":{"detected_after":null,"resolved_after":null},"date_kind":"incident","detection":{"en":"Responders noticed through the alerting noise that the ingest service Shepherd had gone into a crash loop more than an hour earlier. On Saturday morning they saw that the Kafka balancing jobs were not progressing and disk usage had not improved, and once the metadata topics were repaired, SLO processing triggers fired about ~18h of late data.","ko":"대응자들은 알림이 쏟아지는 가운데 수집 서비스 Shepherd 의 크래시 루프를 한 시간 넘게 지나서야 알아챘다. 토요일 아침에는 대응자들이 Kafka 밸런싱 작업이 나아가지 않고 디스크 사용량이 줄지 않는 것을 봤다. 메타데이터 토픽을 고치자 SLO 처리 트리거가 약 18시간 늦은 데이터를 알렸다."},"components":["message-broker"],"root_cause":"human-operation","remediation":[{"text":{"en":"Marked the impacted partitions read-only on the producer side, which stopped Shepherd's crash loop","ko":"망가진 파티션을 프로듀서 쪽에서 읽기 전용으로 돌려 수집 서비스의 크래시 루프를 멈췄다."}},{"text":{"en":"Ran partition reassignments to bring customers back to normal","ko":"파티션 재배치를 돌려 고객들의 수집을 정상으로 되돌렸다."}},{"text":{"en":"Turned off the Kafka autobalancer and left a manual reassignment plan running overnight","ko":"Kafka autobalancer 를 끄고, 균형을 일부 되찾는 수동 재배치 계획을 세워 밤새 돌렸다."}},{"text":{"en":"Reduced local retention on Kafka to rely on S3 tiered storage for longer retention","ko":"자정 무렵 PT 에 브로커 디스크가 너무 빨리 차자 Kafka 의 로컬 보존 기간을 줄이고 긴 보존은 S3 tiered storage 에 맡기려 했다."}},{"text":{"en":"Repaired the broken _confluent-tier-state and __consumer_offsets partitions, which unblocked SLO processing","ko":"_confluent-tier-state 와 __consumer_offsets 토픽의 깨진 파티션을 고쳤다. 그러자 막혀 있던 SLO 처리 서비스가 늦은 데이터를 따라잡기 시작했다."}},{"text":{"en":"Turned off ingest to stop the brokers from running out of disk","ko":"토요일 아침 7시 PT 에 디스크 여유 공간이 5.07% 일 때 수집을 껐다. 디스크 사용량은 멈췄지만 줄지 않았다."}},{"text":{"en":"Turned off tiered storage, which freed the broker disk space at once","ko":"tiered storage 를 끄자 디스크 공간이 바로 돌아왔다. 2021년 예전 장애 때 통했던 방법이었다."}},{"text":{"en":"Reopened ingest, which ended the customer impact","ko":"수집을 다시 열었고, 이때 고객 영향이 끝났다."}},{"text":{"en":"Truncated the fully damaged Retriever partitions and left them read-only","ko":"그날 늦게 완전히 망가진 Retriever 토픽 파티션을 손으로 고쳐 잘라 내고 읽기 전용으로 두었다. Retriever 가 offset 초기화를 감당하지 못해 오래된 데이터를 한동안 조회할 수 없었지만 데이터를 잃지는 않았다."}},{"text":{"en":"Extended the binlog retention of the MySQL read-only replica because Activity Log could not publish to Kafka","ko":"Activity Log 가 Kafka 로 이벤트를 보내지 못하게 되자 Honeycomb MySQL 읽기 전용 복제본의 binlog 보존 기간을 늘렸다."}},{"text":{"en":"Patched Retriever so its nodes could idle live and serve old data despite broken offsets","ko":"offset 이 망가졌어도 Retriever 노드가 죽지 않고 대기 상태로 남아 오래된 데이터를 조회할 수 있게, 작은 수정 여러 개를 넣었다."}},{"text":{"en":"Made a one-time offset reset work in Retriever","ko":"금요일 정오 무렵 Storage 팀이 Retriever 의 offset 초기화 장치가 동작하게 만들었다."}},{"text":{"en":"Evacuated to a new Kafka cluster with new topics and partitions","ko":"새 Kafka 클러스터로 옮기는 마이그레이션을 화요일에 두 시간 만에 큰 문제 없이 끝냈고, 신호 지연은 많아야 한 시간이었다."}},{"text":{"en":"Booted an Activity Log database instance from 9 December and replicated from it to limit data loss","ko":"새 복제본으로 binlog 보존 기간을 늘리려던 방법이 통하지 않자, 12월 9일 화요일 시점의 인스턴스를 띄워 그때부터 복제해 Activity Log 손실을 줄였다."}},{"text":{"en":"Cleaned up the older Kafka clusters","ko":"옛 Kafka 클러스터를 정리하고 EU 리전의 Activity Log 일부를 따라잡았다."}}],"action_items":[{"text":{"en":"Consolidate internal documentation better as the company grows","ko":"조직이 커지는 만큼 여러 곳에 흩어진 정보를 더 잘 모은다."}}],"contributing":["no-safeguard-on-destructive-change","staging-not-representative","alert-noise","manual-action-worsened"],"recurrence_of":null,"symptoms_text":{"en":"On 5 December, during the cleanup after a zonal failover exercise in production, Honeycomb destroyed Kafka brokers on purpose as a runbook step, and several topic partitions lost every broker that held them and became fully leaderless. Any Honeycomb team or environment whose assigned partitions were all unavailable could no longer have its data ingested.\n\nThe damage reached three kinds of topics: the topics that carry all ingested events, some of whose partitions were damaged beyond repair; the topics some consumer groups use to track their position; and metadata topics of Kafka and of Confluent add-ons such as tiered storage, some with fully broken partitions. The ingest service Shepherd had gone into a crash loop more than an hour before responders noticed it through the alerting noise, because it buffered in memory the events meant for the broken partitions. About a third of the partitions used by Retriever, Honeycomb's query engine, were affected, and one of them, about 2.5% of the cluster, lost all its data.\n\nThe Kafka cluster then got steadily worse. Overnight reassignments made no visible progress while broker disk usage kept rising; on Saturday morning the brokers were losing 5% of disk capacity per hour with 30% free, and at 7 a.m. PT, with 5.07% free, ingest was turned off, yet disk usage only stabilised instead of falling. When two broken metadata topics were repaired, SLO triggers fired about ~18h of late data, because the SLO processing service had not seen any new data in that time. On the following Wednesday a topic delete operation timed out, and after that no Kafka broker could do anything administrative except list topics: describing, reassigning or modifying topics timed out.\n\nResponders could not run an ordinary leader election, because a \"dirty\" leader election after losing all brokers of a partition can roll back Kafka offsets, and Retriever refuses to process data when offsets go back. Turning off the Kafka autobalancer, reassigning partitions by hand, lowering local retention and moving smaller partitions did not free disk space. Turning off tiered storage, which an engineer suggested because it had worked in an outage in 2021, restored the disk space at once. Later attempts at dirty leader elections for the Activity Log partitions failed.","ko":"가용 영역 장애 대비 훈련을 마친 뒤 정리 단계에서 런북대로 Kafka 브로커를 없애자, 운영 환경의 여러 토픽 파티션이 리더 없이 남았다. 망가진 파티션만 배정된 팀과 환경은 데이터 수집이 실패했다.\n\n수집 이벤트를 나르는 토픽의 파티션 여럿이 망가졌고 그중 일부는 되돌릴 수 없었다. 컨슈머 그룹이 처리 위치를 기록하는 토픽도 일부 망가졌고, Kafka 와 tiered storage 를 관리하는 Confluent 부가 기능의 메타데이터 토픽도 일부 파티션이 완전히 깨졌다. 그날 밤 정리한 목록에서 Retriever 파티션의 약 3분의 1 이 영향을 받았고, 클러스터의 약 2.5% 인 파티션 하나는 데이터를 전부 잃었다.\n\n수집 서비스 Shepherd 는 깨진 파티션에 쓸 이벤트를 메모리에 버퍼링하다 크래시 루프에 빠졌다. 알림이 너무 많아 대응자들은 이 크래시 루프를 한 시간 넘게 지나서야 알아챘다.\n\n밤새 돌린 재배치는 토요일 아침까지 진척이 없었고 브로커 디스크 사용량은 계속 올랐다. 여유 공간이 30% 남은 상태에서 시간당 5% 씩 줄고 있었다. 수집을 끄고 나서도 디스크 사용량은 멈추기만 하고 줄지 않았다.\n\n_confluent-tier-state 와 __consumer_offsets 토픽의 깨진 파티션을 고치자 SLO 처리 트리거가 약 18시간 늦은 데이터를 알렸다. SLO 처리 서비스는 그동안 새 데이터가 들어온 것을 보지 못하고 있었다.\n\n수요일에 Activity Log 토픽을 지우려 하자 삭제 작업이 타임아웃 났고, 그 뒤 모든 Kafka 브로커가 토픽 목록 보기 말고는 관리 작업을 하지 못했다. describe, 재배치, 수정이 모두 타임아웃 났다."},"updated_at":"2026-11-14T12:33:54.112240Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0286","org_slug":"honeycomb"}