スタイル・エッジ技術ブログ

士業集客支援/コンサルティングのスタイル・エッジのエンジニアによるブログです。

MariaDB10.6.7へバージョンアップしたらハマった。

はじめに

こんにちは。エンジニアになって1年半が経ち、時の流れの早さに驚きが隠せないpeipeiです。
余談ですが、お盆休みは先輩にオススメされたAWS Cloud Questをやって大満足でした。(一部無料で学べるのでAWSで遊びたい方は是非!)

今回の記事はRDSをMariaDB10.3.xから10.6.7へバージョンアップした際にバグが発生し、原因を見つけるのに苦労した話です。
「記事として紹介すれば同じ現象で困っている人のためになるかもしれないよ?」と先輩から助言いただき、それなら書いてみよう!と思い、筆(?)をとりました。

早速始めます。

概要

ある日、AWSから下記のような通知がありました。

After August 1, 2022, Amazon RDS will upgrade all instances running MariaDB 10.5.9, 10.4.18, 10.3.28, 10.2.37 and older versions to the latest minor version in the same major series during a future maintenance window.

この通知によると RDSのMariaDBバージョン10.5.9, 10.4.18, 10.3.28, 10.2.37以下のマイナーバージョンは自動アップグレードがかかるようです。 これにより自社システムの一部ではRDSのバージョンアップが必要になりました。

何も対応しなくても自動的にバージョンアップはされるようですが、突然バージョンアップされてシステムで不具合が発生する可能性を避けなければなりません。

本記事はバージョンアップ対応のDB切り替え手順と
DB切り替え前の事前検証で発生したバグ対応に関してご紹介いたします。

バージョンアップ時のDB切り替え手順

バージョンアップにも様々な手法はあると思いますが、現在のシステム運用では下記の方法が最も影響が少ないと考えこちらの方法を採用しました。

  1. 既存RDSにリードレプリカを作成 システム構成1
  2. リードレプリカを10.3から10.6へバージョンへアップ システム構成2
  3. リードレプリカをマスターへ昇格 システム構成3
  4. マスター昇格したRDSへエンドポイントを切り替え システム構成4

事前検証での品質の担保

最も大切なのは、バージョンアップを行ってもシステムで不具合が起きないということを保証することですね。
上記のDB切り替えを行う前に、検証環境で事前検証を行いました。

「ユーザーにとってはデータベースのバージョンなどはどうでもよくて、動きが替わっていないことが重要!」
これは私の先輩からの有難いお言葉です。

品質を担保するときに事前に行ったことは主に2つです。

  • バージョンアップによる変更点を洗い出す
  • 検証環境をバージョンアップして、動作テストを行う

バージョンアップによる変更点を洗い出す

MariaDBの公式サイトからバージョンアップによる変更点を洗い出し、変更点がシステムに影響を与えそうか調査しました。

今回10.3.xから10.6.7まで一気に上げているので、10.3->10.4->10.6に関わる変更点をある程度抑えておかなければなりませんでした。

↓公式サイト
https://mariadb.com/kb/en/upgrading-from-mariadb-105-to-mariadb-106/

検証環境をバージョンアップして、動作テストを行う

事前に動作テスト項目を準備した上で、検証環境をバージョンアップして一つ一つ動作テストを行っていきました。
これはかなりマンパワーが必要だったので、チーム全員で手分けをして行っていきました。大変でしたがチーム一丸で力を入れて取り組めた経験は良かったかなと思います。

なども含めて細かく動作確認していきました。

バグが発生!!

上記の動作検証を行なっている際に、バージョンアップによる不具合が見つかりました。

↓Laravelのエラー文(イメージ)

Type error: Return value of APP\Http\Controllers\hogeController::getId() must be of the type int, null returned ...  

IDはint型で渡さないといけないのに、nullで渡ってきたよ。というエラーが発生しています。
10.3.x(旧バージョン)ではIDを取得して画面表示できていたのに、10.6.7(新バージョン)に上げると何故かIDが取得できなくてnullで返ってきてしまっています。
Google検索をしても、それらしい事例がなかなか見つけられなかったため、本格的に調査を行っていきました。

調査内容

1. 発行しているSQLは正しい?

↓発行されたSQL(イメージ)

SELECT * FROM hoge WHERE id IN (1, 2, 4, 5, ... 1951, 1952, 1953) AND ... ;

こちらをDBでクエリを直接叩くと、検索結果が全て正しく取得できました。
->よってSQL自体に問題はなさそうです。

2. sql_modeは10.3.x(旧バージョン)と10.6.7(新バージョン)で差がある?

新旧のDBでそれぞれsql_modeを確認しました。

SELECT @@GLOBAL.sql_mode;
STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

また、アプリケーションから接続した際のsql_modeも確認しましたが、同様に差はありませんでした。
->バージョンによるsql_modeの違いはないため、今回の原因となっているとは考えにくくなりました。

3. IN句に指定しているデータ件数が多いことは問題ない?

当初からIN句に指定するIDが多いことは気になっていました。

SELECT * FROM hoge WHERE id IN (1, 2, 4, 5, ... 1951, 1952, 1953) AND ...;

もしかすると、指定するデータ件数が多過ぎたり、データ容量が上限を超えていたりするのではないだろうかという視点で調査を行いました。

発行するSQLを調整して指定する件数を500件に絞ってみたり、2000件に絞ってみたりと調べていくと、IN句に指定するデータ件数が1000件を境にしてエラー発生の有無が分かれていることが分かりました。
999件以下だと問題なく画面表示され、1000件以上だとエラーになっています。

地道に調べていったことで「1000件」というワードが鍵になるということが分かり、Google検索に「MariaDB10.6.7」と「IN句」、そして「1000件」というキーワードを使って、 ようやくバグについて書かれている記事を見つけることができました。

エラーの原因

in_predicate_conversion_thresholdというグローバル変数の影響で、IN句内で1000件以上の値があると結果を空で返してしまうバグでした。(MariaDB 10.6.7特有のバグ)

クエリは正しく発行されていても、Laravel側からDBへ接続してデータを取得する際に、このパラメータの影響でSQLの結果が空のデータセットとして返ってきていたようです。
↓参考記事
https://jira.mariadb.org/browse/MDEV-27937

対処法

このバグはMariaDB 10.6.8以降のバージョンでは解消されているようです。 現在(2022年8月)はMariaDB 10.6.8以上を選択することが可能ですが、当時対応していた時期に選択できた最新のバージョンはMariaDB 10.6.7までだったので、惜しかったということになります。。

その当時は、Laravel側からDBへ接続する際にin_predicate_conversion_thresholdを無効化する設定を加えることでバグを回避しました。

おわりに

バージョンアップ対応には影響調査と動作検証が非常に重要ですね。なんとか期限までに問題なくバージョンアップすることができて良かったです。

☆スタイル・エッジ・グループでは、一緒に働く仲間を募集しています☆
もし興味を持っていただけましたら、採用サイト↓も覗いてみてください!
recruit.styleedge-labo.co.jp