KOBA789 です。
今年もチーム「ソレイユ(osyoyu, koba789, s4ichi)」で ISUCON に Ruby で出場し、敗退しました。
まずは我々の戦法や秘密兵器の紹介から。
伝統と信頼のサーバーサイドプログラミング
ISUCON7 くらいのときから続けている手法で、競技用のサーバーに開発環境を構築し、3人とも同じホストに SSH でログインして同じファイルシステム 上のソースコード を書き換えます。
他人の変更をリアルタイムに確認できることや後述するようにソースコード のバージョン管理が不要なことなどがメリットです。
SSH 時に WATASHI
という環境変数 で自分の名前を渡すことで、同じ isucon
ユーザーへのログインであっても各個人の普段使いの dotfiles がロードされるようになっています。
これは「実家システム」と呼ばれており、チームの生産性に大きく貢献しています。
あいかわらず git ほぼ禁止
全員で同じコードベースを編集しているため、分散バージョン管理は必要ありません。
とはいえ分散しないバージョン管理は便利なため、リモートリポジ トリなしで git を使っています。
また、ISUCON のような切羽詰まった環境では merge ミスなどで時間を浪費するという考えから、branch を使うことも禁止です。
常に歴史が master 一本になるように運用しています。
mamiya2020
お手製の最強デプロイスクリプト 。名前は sora_h パイセンの mamiya にあやかっています。
初めて mamiya と名の付くデプロイスクリプト を用意したのは ISUCON7 のとき で、そのときは競技時間中に osyoyu が書いてくれたのですが、この mamiya2020 は私が昨年 ISUCON10 に向けて開発したものです。
この mamiya2020 は、webapp/ruby
以下のアプリケーションのソースコード は当然のこと、/etc/nginx.conf
や /etc/my.cnf
などもデプロイしてくれるというスグレモノ。
しかも .gitignore を考慮して配布ファイルをフィルタしてくれるので不要なログファイルなどを転送してしまう無駄もなし。
ちなみに、deploy などという名前を使わず、わざわざこんなふざけた命名 をしているのかといえば、Ruby で優勝した sora_h パイセンへの憧れを忘れずにいようとかそういうことはなく、まぁ単にふざけただけです。
それでも今となってはチーム内の大切なユビキタス 言語となっており、競技終了間際では「ベンチ前にマミって!」「マミった!」「ベンチ回します!!」という声が響いています。
isukekka
たぶん詳細は初公開。チーム「ソレイユ」の秘密兵器のひとつ。
ベンチマーク 走行終了後、ベンチマーク 走行中のログを自動で集計・分析し、結果を通知してくれるというもの。
これも私、KOBA789 作。
ざっくり言うと、/initialize
のリクエス トハンドラ内で各種ログをローテート、別プロセスで sleep 75
を開始して、75秒後に溜まったログを分析するために alp
や pt-query-digest
を起動するということになっています。
alp
や pt-query-digest
の出力は autoindex on
な nginx でサーブされており、以下のようにアクセスできます。
isukekka が吐き出したファイル
同時にチームの GitHub リポジトリ にも上記ファイルへのリンクが書かれた Issue が作られ、その通知が Slack に流れます。
この isukekka により、「Slack の通知が来たらそれを見てプロファイリングすればいい」というワークフローが完成しています。
isukit
環境構築便利ツール集。Itamae レシピの集合体。
ISUCON 9 のときに s4ichi と osyoyu が整備してくれたのが発端。
今回は無職時間を有効活用して私がだいぶメンテしました。
我々は前述のとおりサーバーサイドプログラミング戦法を採用しているわけですが、そのためにはいつも使っている様々なコマンドライン ツールが揃っていないと不便です。
それらをチマチマインストールしたり設定していたりしては時間がもったいないので、すべて自動化しています。
また MySQL 8 に強い執着のある我々のために MySQL 公式の mysql -server パッケージをインストールするためのレシピもほぼ自動化されています。
このおかげで、ISUCON11 予選でもなんの迷いもなく MariaDB 10.3 から MySQL 8 に乗り換えることができました。
そういえば元々は Itamae レシピの集合体だったのだけれど、最近はそれらを含む秘密兵器セット全体を指すようになってる、気がする。
osyoyu/stackprof と speedscope
osyoyu/stackprof はその名の通り、osyoyu が改造した stackprof。
speedscope は各種プロファイラの flamegraph をかなりいい感じに可視化してくれるビジュアライザ。
osyoyu/stackprof の詳細は本人が書いてくれる、と思うのだけれど、speedscope での可視化の結果はかなり映えるので貼っておく。
映えるspeedscope。Ruby のプロファイリングができる
yalp
SQL で再実装された alp 。KOBA789 作。
本家 alp は非常に手軽に一方で、集計条件などの細かい設定はできない(またはやりづらい)という特徴があります。
KOBA789 は SQL が得意なため、だったら SQL で書きゃいいじゃんということで書き直しました。再実装をすると、alp の集計内容の筋の良さがよくわかります。
SQL で書き直したことで、 「ユニークユーザー数のカラムも欲しい」みたいな要望にすぐ対応できるようになりました。
うちのチームで「alp」と言った場合、この yalp を指します。
たたかいのきろく
あくまで KOBA789 の視点です。見えてないところでも他のメンバーはなんかやってた。
10:00 競技開始
koba789: CFn スタックを作る
koba789: マニュアルを音読しはじめる
10:20
koba789: マニュアルの通読が終わる
osyoyu&s4ichi: アプリケーション仕様の理解のための議論開始
koba789: インフラ構築開始
10:21 頃
10:28
koba789: 2台目のサーバーに実家環境構築完了
他の2名がいつものエディタでコードを読める環境ができた
10:46
koba789: 1台目のサーバーで完全な開発環境の構築完了
11:04
koba789: すべてのサーバーで完全な環境の構築が完了
1台目のサーバーのコード・設定を他の2台に mamiya で配れるようになった
いつでもトラフィック を分散可能になった
11:10 頃
koba789: osyoyu&s4ichi から読解したアプリケーション仕様の申し送りを受ける
11:30 頃
koba789: ISU のメトリクスの write heavy であることに気づく
koba789: 構造の抜本的な改善ができるか考えるため、read エンドポイントのロジックを読み解き始める
12:30 頃
koba789: graph のロジックに完全に頭をやられる
osyoyu: icon を DB から降ろそうとして盛大にバグらせはじめる
13:00 頃
koba789: icon 周りのバグは nginx の try_files のミスであることを指摘
osyoyu: なお icon 周りでバグが出て悩む
13:30 頃
koba789: icon のバグは認証の不備であること指摘。X-Accel-Redirect を提案
koba789: graph の読解を諦める
koba789: s4ichi に /api /trend は SQL の LATERAL JOIN で解けると提案
14:00 頃
s4ichi: /api /trend を非同期化
非同期化というか、別デーモン化(loop do; sleep; end
)
定期的に JSON をファイルに吐いて nginx に配らせることに
勝手に E-Tag も付いて便利最高
15:00 頃
15:30 頃
koba789: 2台目・3台目も活用する構成にする
s4ichi: calculate_condition_level 消さないと速くならんよと指摘
このへんで mysql2 gem がめっちゃマルチスレッド絡みっぽいエラーを出し始めた
s4ichi: puma から unicorn へ切り替え
16:10 頃
koba789: is_dirty, is_overweight, is_broken を各カラムに分離、generated column で condition_level を埋めた isu_condition2 テーブルを作成
16:30 頃
osyoyu&s4ichi&koba789: isu_condition2 テーブルを使うようにすべてのクエリを書き換え完了
17:00 頃
osyoyu: GET /api /isu がめっちゃバグる
koba789 の提案に従って LATERAL JOIN を使ったが、LEFT OUTER にならなくて悩む
17:15 頃
17:30 頃
koba789: 冷静になるチャンスをもらったと前向きになる
17:54
koba789: 冷静さを取り戻す前に闇雲に apt install redis-server
18:05 頃
18:10 頃
koba789: 冷静になった末、isu_condition2 テーブルの DB 分割を決断
他のメンバーに LATERAL JOIN 使えとか言っておきながら JOIN が使えないインフラ構成への変更を強行(なお競技終了35分前)
18:39
s4ichi: 分割された isu_condition2 テーブルへの対応を完了
過去最高スコアを記録
18:45 競技終了
まとめ
Ruby で出場すると準備含めてやることがたくさんあって楽しい!!!!!
Ruby で戦っているチームだからこそ見える Go の優位性があり、それが見えるからこその戦法とツール開発があります。
たぶんまた来年も Ruby で出ます。