GA

2021/09/24

「MySQLのフェイルオーバーテストをする」と聞いてぼんやり思ったこと

TL;DR

  • 負荷をかけながらフェイルオーバーテストをするなら、負荷クライアント側で「どの書き込みが成功したのか」のログは必ず取っておく
    • でないと、フェイルオーバー起因でデータロストが発生するのかしないのかのチェックができない

フェイルオーバーシナリオ

スイッチオーバー(手動での切り替え)を含めてざっと思いつくのはこれくらい。

  1. スイッチオーバー
  2. mysqldの正常終了
  3. mysqldの異常終了、特に、mysqld_safeやsystemdがmysqldを再起動させてしまう環境
  4. mysqldのハングアップ
  5. カーネルパニック
  6. ファイルシステムのハングアップ
  7. 電プチ

スイッチオーバー

たぶんHAソリューションを作る時にちゃんとテストするからこれはそんなに問題にならない気がするけれど、(レプリケーションベースのソリューションの場合)「レプリケーション遅延が起こってる時のスイッチオーバー」で何が起こるかは観測した方が良い気がする。


概ね、「レプリカが追いつくまでスイッチオーバーが遅延する」か「データをロストしながら無理矢理スイッチオーバーしてしまう」のどちらかに属する気がする。

理想は、「一定以上のレプリカ遅延だったら、稼働系が死んでいるわけではないスイッチオーバーは棄却する」のが良いんじゃないかな。

mysqldの正常終了

これもテストされていると思う。HAの監視が綺麗にECONNREFUSED(111)でスパッと転けるし、絶対に旧系に更新は成功しなくなるから大丈夫系。
ただし、 innodb_fast_shutdown = 0だったり、忙しいMySQLだったりでダーティーページが多かったりすると案外正常終了に時間がかかることもある(それでも先に書き込みできなくなるし接続もできなくなるから大丈夫だと思うけれど)


正常終了したmysqldは上位プロセスによって勝手によみがえることは無いので簡単。

mysqldの異常終了、自動再起動ナシ

バグによるsignal 11(コアダンプを伴う), データ破損によるsignal 6(コアダンプを伴う), OOM Killerによるsignal 9(コアダンプはしない)あたりのパターン。再現させるには kill -6 $(pidof mysqld) でいいと思う。


書き込みは失敗するし勝手に蘇生はしてこないけれど、レプリケーション遅延があった場合はそのぶんはおそらくロストする。そして昇格側でロストを許容してサービスを再開した場合、降格側ではそのデータのまま再組み込みはできない(データが新旧でズレるから)、必ずリストアが必要になるはず。


gtid_executedをちゃんと比較する(旧側だけが持っているGTIDが無ければそのまま戻しても良いと考えられる……ちゃんとsync_binlog=1なら)手順を確立しておくこと、旧側のバイナリログ保全の手順を確立しておくこと、あたりがキモだろうか。semisyncを使っている場合、「旧側が受け付けた更新だけど新側にはあって旧側ではクラッシュリカバリでチョップされる」ことがあり得るので注意。


新側が稼働を開始し始めてから旧側のデータを新側にも伝搬するのはなかなか厳しいものがある…というのは、auto_incrementをガンガン使っているテーブルに書き込みながらこれをやると簡単にわかって良い気がする。仮にauto_incrementだけずらして補完しようとしても、ちゃんとユニークキーが振ってないと「本来1人1レコードのはずが何故か2行ある」みたいなことが起こる。ユニークキー大事。

mysqldの異常終了、自動再起動アリ

コアを吐くか吐かないか、コアを吐くにしてもどれくらいのサイズを吐くかでちょっと変わる(コアファイルを吐き切るまではmysqldプロセスは落ち切らず、mysqldプロセスが落ちきらないと上位プロセスからのmysqld再起動は働かない)

上手く(即死してHAがトリガーされる前にmysqldがクラッシュリカバリまで終わらせて正常復帰)すれば、クラッシュリカバリが終わったそのままの状態で系が復帰できるかも知れない(レプリカ遅延していたとしても、蘇ったmysqldが残りをレプリカに再送できる)。


mysqldが死んだだけでOSが元気ならデータロストの可能性は低い…が、semisyncでbinlogが先行でレプリカに行っている可能性があるのは相変わらず。結局gtid_executedをちゃんと比較しないといけないので手間はそんなに変わらない。

コアの書き出しなどに時間がかかってフェイルオーバーが走る場合、その後旧のmysqldがHA上でどう扱われるのかは確認しておいた方が良さげ。

mysqldの異常終了、自動再起動アリ、データ破損のワーストケース

異常終了最悪パターン。ibdファイルの一部が壊れていて、「そのページに触れた時に何度でも異常終了する」みたいなパターン。
下手するといつまでも壊れたibdを抱えたMySQLが稼働系のままになるので死ねる。再起動をカウントして諦めさせる仕組みが…とか考えるくらいだったら再起動しないような設定にする方が素直だと思った。

mysqldのハングアップ

3306でTCPのACKは返すけど、MySQLプロトコルとしては何もしゃべれないパターン。
gdb -p $(pidof mysqld) でアタッチしてほっとくと再現できる。
シンプルな3306/TCPのポーリングだけだとコイツを検出できないので、ちゃんとMySQLプロトコルをしゃべるタイムアウトつきの死活監視をしようね、って思えるやつ(そして書いたやつが これ だ…)


ちゃんとフェイルオーバーが走るかは最低限チェックするとして、kill -9などで止めを刺すか、ハコをヘッドショットするか、頻度は低いはずだから自動化せずに都度人力で解決するか。

カーネルパニック

カーネルパニックでリブートする設定ならほぼほぼここまでの異常系に含められる気がする。リブートしない場合、何故か3306のタイムアウトに想定より時間がかかったりするので確認したい。


再現するには echo c > /proc/sysrq-trigger (rootで

ファイルシステムハング

書き込みやバッファプールからあふれるSELECTは通らないけど、バッファプールに載っていて.ibdにアクセスしなくてよいSELECTは通ったり、それでもスローログに書こうとするとそこで止まったりと兎に角不思議なことが良く起こるやつ。


xfs_freeze とかで再現させている。俺は諦めている(たぶん、ヘッドショットでハコごと落とすのが無難)

電プチ

たぶんフェイルオーバー自体は問題ないが、設定によってはデータをロストするかも知れないのでそのテスト。
ここでクライアントがどこまで書き込みに成功していたかのログが重要になる。成功したものは全て残っていて、失敗したものは全て失われていないといけない。が、semisyncだと略。
ちなみに shutdown -h now とかは生きているプロセスにsignal 15を投げちゃうので、 echo b > /proc/sysrq-trigger とかでブチっとやってる。

0 件のコメント :

コメントを投稿