{"id":"IL-0392","org":"Wikimedia Foundation","date":"2024-09-18","docs":["https://wikitech.wikimedia.org/wiki/MariaDB/troubleshooting#Data_inconsistency_between_nodes_%22drift%22_/_replication_broken","https://gerrit.wikimedia.org/r/c/operations/cookbooks/+/1074127","https://gerrit.wikimedia.org/r/c/operations/software/spicerack/+/1074111","https://gerrit.wikimedia.org/r/c/operations/cookbooks/+/1074128/1"],"title":{"en":"MariaDB replication of section x1 between eqiad and codfw broke on a missing heartbeat row during a cookbook run","ko":"데이터센터 전환을 앞두고 원형 복제를 설정하던 중 Wikimedia x1 섹션의 eqiad·codfw 사이 MariaDB 복제가 6분 멈췄다"},"domain":["database"],"impact":{"scope":"For 6 minutes replication of the x1 section between eqiad and codfw stopped; x1-based services such as reading lists and echo could have served stale data, with no visible errors","users":"Clients in the Americas","region":"codfw"},"review":{"written_by":"session","reviewed_at":null,"reviewed_by":null},"lessons":{"en":"Circular replication is not a normal working mode, so the first fix only moved the break to the other side; TRUNCATE would have worked better than DELETE because it always replicates as a statement, but it was not considered because it is more dangerous. Running sql_skip_slave_counter was avoided, since it could have made things worse or lost data. The check of the replication channel should also run once all tasks are complete, not only right after setup. The combination of row-based replication, pt-heartbeat and differing heartbeat rows was not described in earlier maintenance or documentation.","ko":"원형 복제는 평소 운영 방식이 아니어서 heartbeat 테이블을 지우는 첫 조치가 문제를 eqiad 에서 codfw 로 옮기기만 했다. TRUNCATE 는 DDL 이라 늘 STATEMENT 모드로 전달되므로 이번에는 DELETE 보다 나았겠지만, 더 위험해서 고려하지 않았다. ROW 기반 복제, pt-heartbeat, 서버마다 다른 heartbeat 테이블 행이 겹친 문제는 이전 유지 보수 기록에 없었다. 자동화의 점검은 설정 직후 한 번이 아니라 모든 작업이 끝난 뒤에도 복제 채널을 확인했어야 했다. Wikimedia 는 어느 호스트에서도 sql_skip_slave_counter 를 쓰지 않아 더 큰 문제나 데이터 손실을 피한 것을 잘된 점으로 꼽았다."},"related":[],"sources":[{"url":"https://wikitech.wikimedia.org/wiki/Incidents/2024-09-18_replication","kind":"runbook","archived":"https://web.archive.org/web/20260616085906/https://wikitech.wikimedia.org/wiki/Incidents/2024-09-18_replication"}],"summary":{"en":"Before the next week's datacenter switchover, Wikimedia ran for the first time a cookbook that changes core databases from eqiad -> codfw replication to circular replication, so data flows back to eqiad when codfw becomes the only active datacenter. On statement-based sections pt-heartbeat writes with REPLACE, an upsert, and the step worked. On the row-based x1 and es sections the REPLACE became an update of an existing row, and because heartbeat rows had been cleaned up, that row existed on codfw servers but not on eqiad ones, so replication on eqiad x1 and es6 stopped with a missing-row error.\n\nThe first fix, deleting all heartbeat table rows, moved the x1 problem to codfw and started the user impact at 21:42:19 UTC. At 21:48:19 replication flowed again after heartbeat replication was skipped with SET GLOBAL Replication_wild_ignore_table='heartbeat.%'. The proper fix was to insert the missing heartbeat row on each affected host individually with binary logging off (set sql_log_bin=0): it was tested on es6, applied to es7 before that section was set up, and finally applied on the x1 codfw hosts in place of the ignore rule. The cookbook finished without further problems.\n\nThe later investigation traced the cause to a configuration change that, together with the cleanup of extra pt-heartbeat rows set up after Orchestrator, left the x1 and read-write es hosts using ROW format without making pt-heartbeat-wikimedia use STATEMENT for its own session. That was fixed in a later deploy.","ko":"Wikimedia Foundation 은 다음 주 데이터센터 전환을 앞두고 2024년 9월 18일 핵심 데이터베이스를 eqiad 에서 codfw 로 가는 한 방향 복제에서 원형 복제로 바꾸는 cookbook 을 돌렸다. 시험을 위해 codfw 를 유일한 활성 primary 데이터센터로 두는 동안에도 데이터가 eqiad 로 자동으로 흘러가게 하려는 것이었다. cookbook 은 STATEMENT 기반 복제 섹션인 s1 부터 s8 까지는 문제없이 끝냈다. ROW 기반 복제를 쓰는 x1 섹션에 codfw 에서 eqiad 로 가는 복제를 설정하고 pt-heartbeat 를 시작하자 문제가 생겼다. 앞선 정리 작업으로 heartbeat 행이 지워져 있어서, pt-heartbeat 의 쓰기가 codfw 서버에서는 기존 행을 갱신했지만 eqiad 서버에는 그 행이 없었다. pt-heartbeat 는 REPLACE 로 upsert 를 하므로 STATEMENT 기반 복제에서는 문제가 없다. 그러나 ROW 형식은 행 쓰기, 또는 행 삭제 뒤 새 행 쓰기만 표현할 수 있다. 그 결과 eqiad 의 x1 서버에서 복제가 \"missing row\" 오류로 멈췄고, es6 섹션도 같이 깨졌다.\n\n원형 복제 모니터링이 아직 켜지지 않아 처음에는 알림이 없었고, 21:28 UTC 쯤 엔지니어가 orchestrator 에서 x1 primary master 가 복제하지 않는 것을 직접 봤다. heartbeat 테이블 행을 모두 지우자 eqiad 쪽 문제는 풀렸지만 문제가 codfw 로 옮겨 가, 21:42:19 UTC 부터 eqiad 에서 codfw 로 가는 x1 복제가 멈췄다. 이것이 사용자 영향의 시작이었다. 21:48:19 UTC 에 heartbeat 복제를 건너뛰는 임시 조치를 넣어 복제가 다시 흘렀다. 이어 binlog 기록을 끈 채 빠진 heartbeat 행을 호스트마다 직접 넣는 제대로 된 조치를 es6 에서 시험하고 es7 에 미리 적용했다. cookbook 은 22:11:55 UTC 에 마지막 ROW 섹션까지 끝냈고, x1 codfw 호스트에도 같은 방식의 조치를 넣어 임시 조치를 대신했다.\n\n나중에 조사해 보니 처음 시험한 고침은 문제를 다른 primary 서버로 옮길 뿐이었다. 원인은 한 변경과 Orchestrator 도입 뒤 생긴 pt-heartbeat 여분 행을 지우는 정리 절차가 겹친 것으로 좁혀졌다. 그 결과 ROW 를 쓰는 x1 과 읽기·쓰기 es 호스트에서 pt-heartbeat-wikimedia 가 자기 세션을 STATEMENT 로 덮어쓰지 않았다. 이 문제는 이후 배포로 고쳤다."},"trigger":{"en":"Running the cookbook sre.switchdc.databases.prepare, which set up codfw -> eqiad replication and started pt-heartbeat, on the row-based x1 and es sections.","ko":"다음 주 데이터센터 전환을 앞두고 cookbook 이 핵심 데이터베이스를 eqiad 에서 codfw 로 가는 한 방향 복제에서 원형 복제로 바꿨다. ROW 기반 복제를 쓰는 x1 섹션에 codfw 에서 eqiad 로 가는 복제를 설정하고 pt-heartbeat 를 시작하자, eqiad 서버에 없는 heartbeat 행을 갱신하려다 복제가 멈췄다."},"severity":"unknown","symptoms":["replication-lag"],"timeline":{"detected_after":null,"resolved_after":"6m"},"date_kind":"incident","detection":{"en":"Engineers watching the first run of the automation saw on orchestrator that the x1 primary was not replicating, because monitoring of circular replication was not yet enabled. The break in the eqiad -> codfw direction was reported by Icinga.","ko":"원형 복제의 일반 모니터링이 아직 켜지지 않아, 엔지니어가 orchestrator 에서 먼저 알아챘다. 복제 자동화가 처음 도는 날이라 평소보다 많은 사람이 데이터베이스 상태를 지켜보고 있었다. 사용자에게 영향을 준 eqiad 에서 codfw 로 가는 방향의 고장은 Icinga 알림이 잡았다. 온콜 호출을 받은 사람은 없었다."},"components":["database","database-replication"],"root_cause":"config-change","remediation":[{"text":{"en":"Skipped replication of the heartbeat tables with Replication_wild_ignore_table='heartbeat.%'","ko":"SET GLOBAL Replication_wild_ignore_table='heartbeat.%'; 로 heartbeat 테이블 복제를 건너뛰게 해 복제가 다시 흐르게 했다."}},{"text":{"en":"Inserted the missing heartbeat row with binary logging off on the es hosts","ko":"es 호스트에는 원래 고장이 난 방향에 맞춰, binlog 기록을 끈 채 eqiad 서버마다 빠진 codfw heartbeat 행을 직접 넣었다. 명령은 set sql_log_bin=0; INSERT INTO heartbeat.heartbeat (server_id, shard, datacenter) VALUES (<codfw-server-id>, 'es6' [or 'es7'], 'codfw'); 였다. es6 에서 먼저 시험했고, es7 에는 원형 복제를 설정하기 전에 미리 넣었다."}},{"text":{"en":"Inserted the missing heartbeat row with binary logging off on the x1 codfw hosts and removed the ignore rule","ko":"x1 codfw 호스트마다 binlog 기록을 끈 채 빠진 heartbeat 행을 넣어, 임시로 걸어 둔 heartbeat 무시 설정을 대신했다. 명령은 set sql_log_bin=0; INSERT INTO heartbeat.heartbeat (server_id, shard, datacenter) VALUES (<eqiad-server-id>, 'x1', 'eqiad'); 였다."}}],"action_items":[{"text":{"en":"Avoid the same issue again, probably by inserting the missing row when it is detected","ko":"같은 문제를 다시 겪지 않게 한다. 빠진 행을 찾으면 직접 넣는 방식을 유력하게 본다."}},{"text":{"en":"Add a post-maintenance health check that replication works in both directions","ko":"유지 보수 뒤 복제가 의도대로 양방향으로 동작하는지 확인하는 상태 점검을 cookbook 에 넣는다."}},{"text":{"en":"Add options to run the maintenance in another order or on each section individually","ko":"유지 보수를 다른 순서로, 또는 섹션마다 따로 돌릴 수 있는 옵션을 더한다."}},{"text":{"en":"Update the Phabricator task for each section rather than only at the end","ko":"Phabricator 를 마지막에 한 번만 갱신하지 않고 섹션마다 갱신한다."}},{"text":{"en":"Fix the monitoring gaps for circular replication","ko":"모니터링의 빈틈을 메운다."}},{"text":{"en":"Made pt-heartbeat-wikimedia use STATEMENT format for its session on the x1 and row-based read-write es hosts, fixed in a later deploy","ko":"x1 과 ROW 를 쓰는 읽기·쓰기 es 호스트에서 pt-heartbeat-wikimedia 가 자기 세션을 STATEMENT 로 쓰도록 고쳐 배포했다."}}],"contributing":["monitoring-blind-spot","test-coverage-gap","manual-action-worsened"],"recurrence_of":null,"symptoms_text":{"en":"On 18 September 2024, while a cookbook was reconfiguring Wikimedia's core MariaDB databases from one-way eqiad -> codfw replication to circular replication ahead of a datacenter switchover, replication in the codfw -> eqiad direction broke on the x1 and es6 sections. Engineers watching the run noticed at 21:28 UTC that the x1 primary was not replicating.\n\nThe replication error was \"Could not execute Update_rows_v1 event on table heartbeat.heartbeat; Can't find record in 'heartbeat'\". The statement-based sections s1 to s8 had completed without problems; x1 and the es hosts used row-based replication, and the row that pt-heartbeat updated did not exist on the eqiad servers.\n\nDeleting the heartbeat table rows fixed eqiad but moved the break to codfw, in the eqiad -> codfw direction that carries real traffic. At 21:42:19 Icinga reported \"PROBLEM - MariaDB Replica SQL: x1 on db2196 is CRITICAL: CRITICAL slave_sql_state Slave_SQL_Running: No, Errno: 1062, Errmsg: Could not execute Write_rows_v1 event on table heartbeat.heartbeat: Duplicate entry 180360966 for key PRIMARY\". For 6 minutes x1 data could have been served stale to clients in the Americas by x1-based services such as reading lists and echo; no visible errors were produced.\n\nThe automation's check after setup was green, because replication broke only after pt-heartbeat was restarted on codfw, and monitoring of circular replication had not been enabled yet, so the first break in the codfw -> eqiad direction was found by people on orchestrator rather than by alerts.","ko":"2024년 9월 18일 데이터센터 전환을 앞두고 cookbook 으로 원형 복제를 설정하던 중, 21:28 UTC 쯤 엔지니어가 x1 섹션의 primary master 가 복제를 하지 않는 것을 orchestrator 에서 먼저 알아챘다. es6 섹션도 같은 상태였다.\n\neqiad 의 x1 서버에서 복제가 \"missing row\" 오류로 멈췄다. 복제 오류는 Could not execute Update_rows_v1 event on table heartbeat.heartbeat; Can't find record in 'heartbeat' 였다.\n\n첫 조치로 heartbeat 행을 지우자 eqiad 쪽은 풀렸지만 문제가 codfw 로 옮겨 갔다. 21:42:19 UTC 에 Icinga 가 PROBLEM - MariaDB Replica SQL: x1 on db2196 is CRITICAL: CRITICAL slave_sql_state Slave_SQL_Running: No, Errno: 1062, Errmsg: Could not execute Write_rows_v1 event on table heartbeat.heartbeat: Duplicate entry 180360966 for key PRIMARY 알림을 냈다. 이때부터 eqiad 에서 codfw 로 가는 x1 복제가 6분 멈췄다.\n\n눈에 보일 만한 오류는 나지 않았다. reading lists 와 echo 를 비롯해 x1 을 쓰는 서비스가 아메리카 지역 클라이언트에게 한동안 오래된 데이터를 보여 줬을 수 있었다.\n\ncookbook 의 설정 후 점검은 통과했다. 복제가 바로 깨지지 않고 codfw 에서 pt-heartbeat 가 다시 시작된 뒤에야 깨졌기 때문이다. 원형 복제의 일반 모니터링은 아직 켜기 전이어서 codfw 에서 eqiad 로 가는 방향의 고장은 모니터링이 잡지 못했다."},"updated_at":"2026-11-14T12:34:04.318592Z","url":"https://benchlet.com/incidentlib/app/cases/IL-0392","org_slug":"wikimedia-foundation"}