こんにちは。Yahoo! MAPアプリでバックエンド(以下BE)を担当している木村です。
Yahoo! MAPアプリでは、店舗検索や経路検索といった基本的な機能のほかにも、クチコミや写真の投稿、閲覧の機能があります。ユーザーからより多くの投稿を集めるため、インセンティブを付与するクチコミ投稿キャンペーンを実施しています。(※キャンペーンは2~3カ月ごとに実施しており、対象外の期間もあります。)
今回は、クチコミ投稿キャンペーン時に生じてしまったメモリ圧迫の問題と、その調査方法や解決策を紹介します。
※~2023年6月30日まで実施しているキャンペーンのメインビジュアルです。
どんな課題が生じたか?
前述の通り、Yahoo! MAPアプリではクチコミ投稿キャンペーンを実施している場合があります。
クチコミ投稿キャンペーン期間中は通常よりもクチコミ投稿が多くなる傾向があり、クチコミ系を管理しているAPIでメモリ使用率が圧迫するという状態になりました。
手動でメモリをクリアする方法は存在しましたが短期間で再び緊迫してしまい、とても手間がかかっておりました。加えて発覚したのは12月上旬だったため、年末年始までに改善できない場合は出勤の可能性も上がっていました。
そのため、短期間で安全にAPIのメモリ使用率を改善する必要がありました。
Java言語の特徴
前提として、APIの言語はJavaで実装されています。
Java言語の魅力的な特徴の一つは、開発者がオブジェクトのライフサイクルを管理しなくても良いという利点があります。オブジェクトは必要な場合に自動的に生成され、不要になればJVMにより自動的に解放されるためです。
今回のようにメモリ使用率が緊迫することは、適切にGC(ガベージコレクション)できていないという予想が立てられます。
どの様な調査を実施したか?
具体的な調査内容は下記の様な形で実施しました。
- メモリ使用率が増加したタイミングを把握
- Javaのメモリ使用率などを確認できるツールで調査
1. メモリ使用率が増加したタイミングを把握
今までのメモリ使用率を確認したところ、かなり以前からメモリ使用率が右肩上がりに緊迫する傾向にあることがわかりました。適切にGCができていない実装を加えてしまったリリースタイミングを見つけられればよかったのですが、今回は発見できませんでした。
加えて、手動でメモリをクリアした地点からメモリ使用率が上がり、85%ほどでメモリ使用率が落ち着く傾向にあることもわかりました。
2. Javaのメモリ使用率などを確認できるツールで調査
Javaにはパフォーマンスを測定するツールが複数存在し、ヒープダンプを取得することでメモリ使用状態などを確認できます。
しかし、結論から記載すると、ツールを利用しても今回はドミネーター(ヒープの中で保持メモリ使用量の多いオブジェクトのこと)を見つけ出すことができませんでした。 振り返ってみると限られた時間の中で調査する上で下記の様な課題があったかと考えています。
- ローカル環境で動作確認を実施したため、実際の環境とは乖離(かいり)が存在する
- ヒープダンプの解析にはある程度の慣れと根気が必要だが、慣れていなかったことに加えて時間も限られていた
調査の結果どうしたか?
調査の結果、かなり以前からメモリ使用率は増加傾向にあるが一定の閾値で落ち着く傾向にあり、ローカル環境でドミネーターを発見することはできなかったという残念な結論に至りました。
Java言語の特徴に立ち返ってみると、JVMのヒープは全体の50%〜60%程度のメモリを占有しています。従って、ヒープメモリの最大サイズを減少させることとはJavaのメモリ使用量を減少させることにつながります。
今回は下記の観点からJavaの「-Xmx」オプションを利用して、ヒープの最大サイズを減らす方針に決めました。
- ヒープの最大サイズを減らすことはメモリ使用量を減らすことにつながるため。
- 実は他のチームでヒープの最大サイズを減らすことでメモリ使用率が改善された実績があったため。
- 年末年始の安定稼働を目指すため
どの様な形で素早くリリースを実施したか?
当然ですがヒープサイズを調節した結果、動作に問題ないかを確認しなくてはいけません。ヒープサイズが小さくなることでGCの回数が多くなるため、CPU使用率の増加やLatencyの悪化などが考えられました。
Yahoo! MAPアプリにはステージング環境が存在するため、まずはステージング環境で動作確認を実施しました。Pythonでスクリプトを組み立て、APIに対して本番相当の負荷をかけてエラーが発生しないか、あるいはLatencyが悪化しないかなどを確認しました。
ステージング環境では問題なかったため、いよいよ本番環境へリリースしていくことになりました。
対象APIはマルチリージョンで管理されているため、片方のリージョンでリリースを実施し、問題が生じた場合はリバートする方法を取りました。社内ではCI/CDツールとして、ScrewDriver.cdを利用して利用してさまざまな環境にリリースを実施しております。CI/CDを利用すれば リバートの方法は簡単で、ボタンをポチッと押下するだけです。
(画像はイメージです。)
片方のリージョンでメモリ修正改善後のAPIをリリースし、動作させておりました。本番環境で問題ないかの確認は、CPU使用率やRPSなどをUI上から確認できる社内PFを利用し動向を見守りました。
結果
リリース後の結果としては、LatencyやCPU使用率などは安定している状態で、最終的なメモリ使用率を改善させることができました。
おわりに
調査と執筆にあたり、下記書籍の内容を参考にさせていただきました。
https://store.shopping.yahoo.co.jp/bookfan/bk-4873117186.html
昨今、マシンのスペックが上がってきていることでJVMの仕様などをあまり考えずに開発を進めることができる傾向があります。しかし、今回この様な課題に直面したことでJavaの特性に関して勉強することができました。しかし、いまだドミネーターを見つけることはできておらず、暫定的な解決となっております。今後はDynatrace(マイクロサービス全体のパフォーマンスを分析するツール)を導入し、調査する予定となっております。
今回サポートいただいた先輩たちや暖かくナレッジを共有いただいた社内の皆さんには感謝しかありません。加えて、CI/CD環境が整っていたからこそ、達成できた取り組みだったと思います。
最後になりますが、ぜひヤフーマップを利用してクチコミ投稿をしてみてください!最後までお読みいただきありがとうございました。
こちらの記事のご感想を聞かせください。
- 学びがある
- わかりやすい
- 新しい視点
ご感想ありがとうございました
- 木村 勇斗
- Yahoo! MAPアプリ エンジニア
- Yahoo! MAPでAndroidとBEの開発を行っております。マイブームにハマると凄いタチで、よさこいや手品にハマってきました。最近はサウナにハマっています。