Java
Spring-boot
Spring Batch
MyBatis
それはある日突然に・・・
まぁ現実には突然にというわけではないんですが。 とある現場で基幹システムの開発プロジェクトをやっていました(フルスクラッチ)。
で、そこで私自身は本体側というか業務系アプリケーションやバッチの開発に携わっていて、移行チームが別部隊で動いていてました。 ただこの基幹システム全体のシステムアーキテクチャに絡んでいたり、移行チームが開発するバッチのコードベースも自分が書いたこともあって、移行チームがどういうものを作っているかは何となく把握はしていました。
またプロジェクト的にいきなり新システムに移行するのは色んなリスクがあるよねってことで最終的に本番昇格する環境を作り、一定期間は現行システムと並行運用してシステムだけでなく業務としての正しさみたいなのも評価した上でローンチしましょうという手筈でした。
そしていよいよ本番昇格環境で移行バッチを走らせた日、問題が発生したわけです。
移行バッチが動かない
移行チームのリードエンジニアが私のところに飛んできて言いました。 「i-zackyさん、移行バッチが動きません・・・」と。
もちろん下位環境というかいわゆるステージング環境と呼ばれるところで動作検証はしていたそうなのですが、本番のデータボリュームを相手にするのはこの時が初でした。
とりあえずバッチのログを見てみようってことでCloudWatchに吐かれているログを見にいくと、そこには軒並みQueryTimeoutの文字が・・・。
SQLのタイムアウト設定を伸ばす
確かにMyBatisに設定していたクエリタイムアウトの時間は3分とかにしてたので、ここの設定を15分あたりまで伸ばしてみました。まぁバッチだしということで。 (そんな重たいクエリはそもそも打つなという私からの意思表示でもあったわけでもあったんですが)
これによってQueryTimeoutの問題が解消したバッチがちらほら出てきて、よしよしと思っていた矢先、バッチが軒並みコケ始めます。
あらためてログを見にいくと、今度は軒並みOutOfMemoryErrorの文字が・・・。
なるほどね。 こういう時の定石として「まずは物理(札束)で殴れ」と教わってきた私はECSタスクに割り当てるvCPU / メモリの増強、つまりスケールアップを試みることにします。
いくら金を積んでも動いてくれないバッチ
当初、各移行バッチのECSタスクに割り当てていたリソースはvCPUが4GB、メモリが8GBでした。
とりあえず倍にしてみようということで、vCPUを8GB、メモリを16GBにスケールアップしてバッチを再実行するも変わらずOutOfMemoryErrorやQueryTimeout。
じゃあメモリを32GBにしてみる→ダメ
それならメモリを48GBにしてやる→ダメ
えーい、vCPUを16GB、メモリを64GBにしてやる!→ダメ
おい、ここまで物理で殴って動かないってどうなってんだ、何か根本的におかしいぞ・・・
そりゃ動かないよな
物理で殴ってびくともしないのがおかしいと思い、ここで移行バッチの実装を見ることにしました。
データ移行のバッチなので移行元の現行システムのテーブルにSELECTして、移行先の新システムにINSERTするというのが大枠の流れです。
ただ現行システムと新システムではデータモデルが違うので、
- 現行システムでは1レコードだったものが新システムではNレコードになる(横持ち→縦持ちの変換)
- 現行システムではNテーブルだったものが新システムでは1テーブルになる(データの集約)
- 現行システムでは数値として持っているものを新システムでは文字列にする(型の変換)
というデータ移行であるあるなことはしていました。
そして最初にログを見た時にQueryTimeoutが出ていたこともあったので、まず現行システムからSELECTしてくる箇所を見ると、そこに実装されているSQLはほぼ全てがWHERE句なしの全件SELECTでした。
ここで移行チームに「移行元ってデータ何件くらいあるの?」と聞いてみると、
「多いもので1億〜3億、やばいやつは10億以上あります」
との回答が・・・
は!?そんなもん全件SELECTしたって返ってくるまで何分かかるかも分かんないし、返ってきてもそんなクソデカデータどんだけメモリあっても足りんわボケェ!!!!