ONLINE_UNCERTAIN: когда MySQL-оператор и база не согласны
Router флапает, оператор два дня висит в блокировке, CRD застыл на 2/3 — а все три члена InnoDB Cluster здоровы. Лечится одним rescan метаданных.
В Optimacros трёхнодовый MySQL InnoDB Cluster под управлением Oracle MySQL Operator обслуживает маркетинговые сайты компании. Однажды утром цепочка симптомов оказалась глубиной в четыре слоя, и каждый слой указывал в другую сторону.
Началось с «router падает по OOM». Потом поды MySQL Router перезапускались в Error. Потом «база деградировала». Кастомный ресурс кластера показывал ONLINE_UNCERTAIN, два инстанса из трёх онлайн, а lastProbeTime не двигался двое суток. Оператор каждые две секунды писал not ONLINE про одного члена и Cluster status results inconsistent.
Спроси базу, а не оператор
Очевидным следующим шагом была бы пересборка «мёртвого» члена. Вместо этого я спросил Group Replication напрямую, read-only, на primary:
SELECT member_host, member_state
FROM performance_schema.replication_group_members;
Все три члена были ONLINE. Якобы сломанный вернулся в группу через incremental recovery ещё двое суток назад. База была здорова. Здорова не была картина у оператора.
Почему оператор врал
Datadir этого члена незадолго до того пересобирали, а пересобранный или клонированный инстанс MySQL всегда получает новый server_uuid. Group Replication знала его по новому UUID. Схема метаданных кластера всё ещё хранила старый.
Оператор сопоставляет членов Group Replication с метаданными по UUID. Он искал старый UUID, не находил его онлайн, объявлял члена упавшим и отдавал «несогласованный» статус. Хуже того, всё это время он держал busy-lock кластера: его обработчик статуса сделал больше 27 000 повторов с блокировкой, принадлежащей этому члену, отсюда и замёрзший кастомный ресурс. Сам MySQL Shell в cluster.status() говорил прямо: OK_PARTIAL и ошибка инстанса с подсказкой cluster.rescan().
Router флапал, потому что показанная ему топология выглядела нестабильной. Все симптомы выше были настоящими, но ни один не был причиной.
Исправление
Сначала бэкап только схемы метаданных через mysqldump --single-transaction. Затем с primary:
dba.getCluster().rescan()
rescan() приводит метаданные в соответствие с реальностью: записал новый UUID члена и удалил осиротевшую учётку для recovery. Он трогает метаданные, а не данные приложения, и он идемпотентен. Оператор сразу получил согласованную картину, отпустил блокировку и вернул кастомному ресурсу ONLINE с тремя инстансами. Если бы блокировка задержалась, следующим шагом был бы рестарт Deployment оператора.
Чего не делать
За несколько дней до этого я пересобирал членов этого кластера по-настоящему, пересоздавая тома, потому что их данные были повреждены. Эта память делала пересборку соблазнительной и здесь. Это была бы ошибка: член уже вернулся в группу, устарела только бухгалтерия. Пересборка здорового члена добавляет риск и ничего не чинит.
Что вынести
- Для InnoDB Cluster истина —
replication_group_membersна primary..statusоператора — это мнение, которое может замёрзнуть. - Пересобранный или клонированный член всегда получает новый
server_uuid. Сделайcluster.rescan()стандартным последним шагом любого восстановления члена. - Замёрзший
lastProbeTimeи счётчик повторов в десятки тысяч означают застрявший контроллер, а не сломанную базу. - Бэкапь схему метаданных перед тем, как её трогать, даже если операция задокументирована как безопасная.