私たちは、2020年9月にNTTコミュニケーションズ株式会社(以下、NTT Com)とNTTリミテッド・ジャパン株式会社の有志メンバーが開催したチューニングコンテスト「NTT Performance Tuning Challenge」で2位に入賞したレゾベアーズです。今回は、競技者としての視点でNTT Performance Tuning Challengeにどのように挑戦したのかについて、ご紹介します。
※NTT Performance Tuning Challenge 2020のレポートや課題解説はこちら
レゾベアーズの紹介
NTT Performance Tuning Challengeに参加する際には、最大3人で1チームを編成することができます。私たちは、NTT Com プラットフォームサービス本部 アプリケーションサービス部の熊谷直也とNTTレゾナントの井手理菜、岩田紘成の3人で参加しました。コンテストで利用できる言語はGo、Python、Node.js、Rubyが用意されましたが、チームメンバー全員が一番なじみのあるPythonを選択しました。
コンテスト当日までの準備
コンテストの数日前から、NTT Performance Tuning Challenge事務局より昨年度の問題をベースにした練習環境が提供されました。その環境下で、当日実際に利用するであろうパッケージのインストール手順やミドルウエアの設定方法などをまとめる作業を行い、アプリケーションコードを管理するGitHubリポジトリなども用意しておきました。
当日の様子
前半戦
事前にしっかりと準備していたため、立ち上がりは比較的スムーズに対応できました。GitHubでのコード管理、ApacheからNginxへの載せ替え、ログ解析に用いるalp、pt-query-digestのインストール、各種ミドルウエアの自動起動有効化などの作業を、開始30分以内に終わらせることができました。
まずは課題のWebサービス「n-plus」をブラウザで一通り使ってみた後、実際にベンチマークを走らせたところPythonでの初期スコアは300程度でした。
次に、ログの分析やアプリケーションコードの読み込みを行い、次のような問題があることが分かりました。
(1)N+1問題を引き起こすクエリがある
(2)Indexがテーブルに全く貼られていない
(3)画像データをデータベース(以下、DB)で管理しており、リクエストの度に読み込んでいる
(4)巨大なJSON Web Token(以下、JWT)の失効トークンリストをテキストファイルで管理しており、こちらもリクエストの度に読み込んでいる
特に(3)は、メンバーの誰も改修作業をやったことがなかったため、まずはそれ以外の問題の改修を優先して行いました。その後、MySQLのslowログに出てきたクエリを参考にインデックスを貼るなどDBのチューニングを行いながら、アプリケーションコードの改善を行っていきました。
中盤戦
簡単なN+1問題を改修しつつ、前回の記事(レポートや課題解説)でも詳しく紹介している(4)のJWTの失効トークンをテキストファイルで管理している問題の解決に着手しました。テキストファイルではなくインメモリで管理するため、memcachedをインストールしてアプリケーションコードの改修を行いました。提供された仮想マシン(VM)に複数のPythonバージョンがインストールされており、python-memcachedモジュールを誤ったバージョンにインストールしてしまうなどのトラブルもありましたが、なんとか無事に改修を終えて、スコアを向上させることができました(この時点でのスコアは3000超)。
後半戦
今回用意されたサーバーは3台でしたが、全ての処理をメインの1台で行っていたため負荷分散に取り組みました。
まずは、ミドルウエアの分散としてMySQLを別サーバーへ外出しし、アプリサーバーとDBサーバーを切り分けました。次に、練習会でも取り組んだNginxの冗長化として、ベンチマークで受けるサーバーのリクエストを3台に分散させる設定を行いました。
練習時にサーバー台数を3台に増やしたところスコアが3倍に純増したため、今回もスコアの大幅アップを期待していましたが、いざベンチマークを走らせてみると、むしろスコアが下がってしまいました。今回、ボトルネックがDBの画像読み込みの部分だったため、リクエスト数が増えたことで、かえって負荷がかかってしまったようです。残り時間30分ほどになってしまったため、画像などの静的コンテンツの外出しは諦めざるを得ませんでした。
終了直前
終了30分前あたりで画像以外の問題は大方対応できたので、「キャンペーンモード」の調整に移りました。今回のお題では、「キャンペーンモード」という概念があり、これを調整することによりベンチマーカーからのトラフィックが変化するようになっていました。
モードは1、2、3と3段階用意されており、1は「販促なし」、2は「販促あり」、3は「大規模販促あり」、と数字が大きくなるにつれてリクエストも増えるという仕様でした。デフォルトではキャンペーンレベルが1になっていたので、試しに2に上げてみたところ、スコアが6000を超えて大きく向上。さらに3に上げてみましたが、レベル3のリクエスト数に耐えきれるほど改修が追い付いていなかったようで、逆にスコアは下がってしまいました。
そこで、最終的に一番高スコアを記録した、キャンペーンレベル2でいくことに。コンテスト終了後の最終負荷テスト時に問題なく動作するのかについて念入りに確認をして、コンテストは終了しました。
結果発表
スコアボードが閲覧できなくなる直前のスコア状況から、最終負荷試験で正確に動作をすれば3位以内には入るはず……と思いながら、待つこと30分。NTTテクノクロス社のメンバーで構成された「新米」チームに敗れて優勝は逃しましたが、2位に入賞することができました!!
コンテストを振り返って
【よかった点】練習の成果が出せた
以下のような練習をすることで、練習したことを全て時間内に行えました。
- ApacheからNginxへの載り換え
- Nginx、MySQLのログの視覚化から改善ポイントを検討
- MySQLのインデックスを利用したクエリの高速化
- アプリケーションコード上のN+1問題の解決
- メモリキャッシュを利用した高速化
- 負荷分散(構築はできたが、画像問題で効果的に動作しなかったため不採用)
- 仕様をしっかり読んでキャンペーン機能に気付けた
【よかった点】リモート環境下でもコミュニケーションを円滑にとれた
今回のコンテストはリモートでの開催だったため、チーム内でのコミュニケーションが大きな課題でした。しかし、練習会や直近の開発業務で効果が高かったDiscordの常時通話状態で対応したところ、コミュニケーション・作業共にスムーズに行えました。来年は、NTT Comが開発したオンラインワークスペース「NeWork」を利用するのも良さそうです。
【反省点と次へのトライ】頻出パターンの対策が不十分だった
今回対応できなかった「DBに静的コンテンツ(画像など)が保存され、その読み込み部分がボトルネックになっているため、静的コンテンツを外出ししてWebサーバー側でキャッシュして高速化する」というのは、LINE株式会社が開催している「ISUCON」などでも出題されたことがあるパターン。しかし、その練習をできておらず、いざ本番で「これ、知ってるやつだ!」となっても後回しにしてしまい、結局時間内に解決することができませんでした……。練習できなかったことは本番でもまず対応できないので、今回できなかった問題を含め、頻出パターンをしっかり復習しておきたいと思います。
【反省点と次へのトライ】エラーログを、おろそかにしてしまった
コンテスト中盤、ベンチマークが失敗ログを吐いて終了してしまい、しばらく正常なベンチ実行ができませんでした。慌ててコードの切り戻しやDBのデータ初期化などをしてしまいましたが、よく見ると、失敗ログにしっかりとエラー内容が書いてありました。当たり前のことですが、予想外のトラブルがあった時こそ、落ち着いてログを見ることが大切だと痛感。このことは、日々のトラブル発生時においても生かしていきたいと思います。
まとめ
今回、優勝することはできませんでしたが、参加することで、Web技術やノウハウの習得はもちろんのこと、リモート環境下での問題解決ノウハウなど多くの学びがあり、非常に有意義で楽しいコンテストでした。参加された皆さんも、お疲れさまでした! そして、運営事務局の皆さん、本当にありがとうございました!!
NTTグループでは、NTT Performance Tuning Challengeの他にも、アルゴリズム能力を競う社内プログラミングコンテスト「NTT Coding Challenge」など、日頃培った技術力を試せるコンテストを定期的に開催しています。
こうした機会では、普段全く接点のないNTTグループ内のエンジニアと交流できるだけでなく、一人ひとりがそれぞれで行っている日々のスキルアップを一緒に行うことができます。コンテストがきっかけとなり社内でチームを作り、本家のISUCONに参加したり、チーム内で競技プログラミングに関するノウハウを共有する場が生まれたりもしていますし、スキルアップに向けたモチベーション向上につながり、非常にありがたいと感じています。その一例として、NTTレゾナントでは昨年度からベトナムのオフショア開発を進めており、実際に現地のエンジニアとアルゴリズムの勉強会なども定期的に開催しています。
ベトナムとの勉強会の様子
今後もこうした社内コンテストに積極的に参加することで、NTTグループ内のエンジニアと切磋琢磨しながら技術力を磨いていきたいです。また、いずれは、運営側に立って貢献できたらとも思っています。