🏋️‍♂️

『みんなで解く ISUCON勉強会』を開催しました!

2023/10/19に公開

ラブグラフのエンジニア兼CTO、横江 (@yokoe24) です。
2023/10/14(土)に、『みんなで解く ISUCON勉強会』 を開催させていただきました!

https://lovegraph.connpass.com/event/298414/

きっかけは 『ISUCON 夏祭り 2023』 というイベントに参加させていただいて、
そこでのハンズオンが楽しく、「せっかく教わったからには自分でも解いてみたい!」と思ったからです。

https://isucon.connpass.com/event/288820/

とはいえ、ハッカソンとかISUCONのような時間に迫られるタイプのコンテストって
自分の中では苦手意識があって、ゆるーく解いてみたいな〜という気持ちがあったので、
それが今回のイベントとなりました。
(わかりにくいと思って変えましたが、実は最初は 『ゆるふわISUCON勉強会』 というタイトルでしたw)

準備編

問題に @showwin さんの ISHOCON1 を使おうということは早い段階で決まっていました。

ISUCONは3人チームで取り組むことを基準に課題が作られていますが、ISHOCONは1人で8時間かけて解くことを基準に難易度を設定しています。

ということで挑戦しやすそうでしたし、サーバーの設定も Terraform で出来るようになっていて準備もしやすそうでした。

そして、イベント開催にあたっては
ISUCONという名称を使用したい場合のガイドラインと申請フォーム に従って申請する必要があります。(※企業が主催する場合)

これについて知らなくて、親会社である MIXI のかたに教えてもらって大変助かりました……!!🙏
すぐの承認をくださった @941 さんもありがとうございました!!❤️

そして前日の深夜からサーバー構築をおこないました。
IAM Identity Center を初めて使ってみたら terraform plan や apply が上手く動かずワタワタしました。(ここらへんの問題
なんとか間に合ってよかったです。

当日編

私含めて5人が集まりました。
みなさま朝からの集合ありがとうございます!

  • yokoe:運営メンバー。ISUCONは1回のみ経験
  • hiro:運営メンバー。ISUCON未経験。前日にISUCON関連の記事を読んで予習してきたそう
  • haruki:ISUCONは1回のみ経験
  • kuzushiki:ISUCONは未経験。ISUCON13に向けてfujiwaraさんの本などで勉強中
  • kazuya:ISUCONは未経験。ISUCON夏祭りに少しだけ参加

と、いい感じに同じレベルくらいの方々が集まりました!

SSHログインやアプリケーションサーバーの起動方法について案内したあとは、
各々で発見を共有しながら解いていきました。

私は特に kuzushiki さんにお世話になりまして、
alp を導入するための方法を教えてもらったり、
画像の読み込みに時間がかかる問題について、nginxによる静的配信で解決する方法を教えてもらったりしました。
ありがとうございました!🙇

各々の最終的な最高スコアは

  • 57815
  • 39107
  • 35524
  • 28000前後
  • 15000前後

となりました!
初期状態でのスコアが 261 でしたので、みなさん大きくスコアを伸ばしています!

57815点を取ったのは私で「やったぜ!」と思っていたのですが、
他のブログ記事を見ると10万点超えのかたもいらっしゃるのでまだまだですね・・・😶

やったこと

1. インデックスを貼る(261 → 16364)

hiro さんが「インデックス貼ったらめっちゃスコア上がった!」と話されていたので
自分もインデックスを貼ってみたらめっちゃ上がりました!

桁が2つも変わったので、自分的にはこの時点でもう大満足でした(笑)

2. Ruby のバージョンを 3 系にしてみる

Ruby 2.7.1 で動いていましたので、
rbenv で戻せるよう気を付けつつ、Ruby 3.2.2 に上げてみました。

これはあまり効果なしでしたね。

3. 静的ファイルを nginx から配信(16364 → 24960)

画像が全部で4MBくらいあるなぁー、とボヤいていたら
kuzushiki さんが教えてくれました。

/etc/nginx/nginx.conf

location /images {
    alias /home/ishocon/webapp/ruby/public/images;
}

を書くことで解決です!

sudo systemctl restart nginx

をしないとコンフィグファイルの更新が反映されないんですが忘れがちでした……。

なお、途中で

- location /images {
+ location /images/ {

と書き換えてしまった結果、
ベンチマークのスコアが -359387点 になるということがありました!(笑)

最初ベンチマークのバグを疑ってしまったのですが、マニュアル

スコアはベンチマーカーが1分間の負荷走行を行っている間にレスポンスが返された (status code 200 * 1点) - (status code 4xx * 20) - (status code 5xx * 50) により算出されます。

と、しっかり記載されており、実際アクセスログを見ると 404 が大量に出ていました・・・😇

4. current_user メソッドでDBアクセスをおこなわないように(24960 → 28826)

ログイン時に user_id だけをセッションに保存し、
current_user メソッドは毎回 users テーブルから user_id をもとに
user を取得する処理になっていたので、セッションに user ごと保存するように変更しました。

5. トップ画面の N+1 を解決(28826 → 38286)

テンプレートファイルを見ても N+1 はなさそうだな〜と思っていたのですが、
よく見たら each 内で SQL を毎回呼んでいる箇所を見つけ改修しました。

いつも N+1 の解決を ActiveRecord に頼っていたので、
SQL の書き方などで防ぐのは頭を使いました!

6. sinatra のバージョンアップ(38286 → 46639)

bundle update しても動くかな? と、なんとなく思って動かしたら、
スコアが上がってびっくりしました!😲

sinatra が 2.0.8.1 → 3.1.0 に上がるとともに
mustermann が 1.1.1 → 3.0.0 に上がったので、
もしかしたら後者が起因してるのかもしれません。

ライブラリのバージョンアップも意外とスコアアップに大事な要素なんですね!

7. unicorn から puma へ載せ替え

haruki さんが puma に換えてマルチスレッドにしたことでスコアが上がったとのことで試したのですが、
自分の場合は変わらずで unicorn に戻しました。設定が悪かったかも?

8. SQLで不要なデータを取得しないように

1件だけの取得なら LIMIT 1 を指定したり、
SELECT * でなく SELECT users.id, users.name のように取得するカラムを指定するなどしましたが、
これは思いのほかスコアにあまり影響がありませんでした。

また、 product ページで

comments = db.xquery('SELECT * FROM comments WHERE product_id = ?', params[:product_id])

として comments を取得しながらもテンプレート側で使わない、という実装を見つけたので
この処理を削除したのですが、それもスコアにはあまり影響がありませんでした。

9. 不要な histories=購入履歴 を取得しないように(46639 → 51173)

user ページで購入履歴を 30 件しか表示しないのに
SQL では全件取っていた箇所に LIMIT 30 を使うことにしました。

10. products テーブルの内容をファイルにベタ書き(51173 → 57815)

products テーブルに更新がかからないことに気付いた私は、
「これはどこかに格納しちゃったほうがいいのでは?!」と思うも、もう終了まで残り20分ほど。

そこで、この記事 に従って、
MySQL Shell を活用してデータをすべて JSON で出力し、そのデータ1万件を定数 PRODUCTS に突っ込むという暴挙に出ました。

これによって

products = db.xquery("SELECT p.id, p.name, p.description, p.image_path, p.price FROM products as p ORDER BY id DESC LIMIT 50 OFFSET #{page * 50}")

products = PRODUCTS.reverse.slice(page * 50, 50)

と書き直すことができ、土壇場でよりスコアを上げることが出来ました!

絶対に仕事でやらない解決手段ですね😹

他にやるとよかったこと

Memcached や Redis を用いたキャッシュ戦略をやりたかったなぁと思ったほかに、
終了後の振り返りで出た、参加者の方々の効果があったことを紹介します。

1. CSS も静的配信

言われてみれば当たり前なのに気付けなかったー!!
images しか静的配信していませんでした。

ただ、ファイルサイズが大きくないためか、
あとで試してみたところスコア的には差が出ないところでした。

2. アプリケーションサーバーを production モードで動かす

production モードで動かすだけで最適化されてスコアが上がるそう。

実際、いま --env production を指定して unicorn を起動するようにしたら、
スコアが 57815 → 62043 に上がりました!
めっちゃ大事ですね!

3. SQL の LEFT 関数を活用する

テンプレート側で「最初の30文字しか表示しない」のような作りの場合、
DBから渡す時点で LEFT 関数を活用することでスコアが大きく上がったとのこと!

カンタンにできるのでこれもマネしたいですね!
LEFT関数、初めて知りました!

https://sql55.com/t-sql/sql-server-built-in-string-function-1.php

終わってみて……

すっっっごく楽しかったです!(大声)

どうやったらスコアが上がるのか頭を悩ませて、
挑戦してみてそれが上手くいくと大きくスコアが上がる、というのがとてもパズルゲーム的で気持ちいいです。

エンジニアリングの楽しいところを煮詰めたような遊びだと思いました。

参加してくださった方々全員が、他の人から学びを得られる勉強会になっていてとてもよかったです。
お昼ごはんやイベント後の夕食もご一緒できて楽しかったです。

ISHOCONには 2 がありますし、
またこの勉強会を開催できる日が来れましたらうれしく思います!💖
ありがとうございました!!!

ラブグラフのエンジニアブログ

Discussion