← Все статьи

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 и счётчик повторов в десятки тысяч означают застрявший контроллер, а не сломанную базу.
  • Бэкапь схему метаданных перед тем, как её трогать, даже если операция задокументирована как безопасная.