名もなき未知

エンジニアリングとか、日常とかそういうのをまとめる場所。

ISUCON11に参加しました(決勝進出したらしい)

久々に参加記です。undefinedというチームで参加して、最終スコア141453を得て決勝に進出することになったらしいです(ええ….)。

以下、時系列順に振り返りです。

5月ごろ

チーム決めのため、友達と6人集まったので2チームで参加することに。目的としてはISUCONを通してミドルウェアへの知見やパフォーマンスを上げていくためのチューニングを学ぶためで、順位は半分くらい行けばいいな~くらいのテンション。

抽選の結果、去年一緒に組んでいた pytran さんとは別のチームで参加することになり、 @mosmos_21@pxfnc と出ることに。

言語Rustにして、7月中を勉強期間、8月は練習会とミドルウェアをやりたいですね~という話をして準備を進める。

6月ごろ

ISUCON申し込みの日あたりは前職の最終出社日の後だったので、ビートマニアの椅子待ちをしてる時くらいにささっとやる。

すぐ枠が埋まったので迷わず申し込んで本当に良かった。あと業務中だからあとで~とか考えずに申し込む勢いも持っておかないと、今後は申し込みバトルに負けそう。来年は無理にでも時間を空ける(空けた分の時間は他でカバーすればいいかなと思う派で、来年生きていて参加できる保証もあるわけではないが)。

7月

転職してゆるゆるRustを勉強していた。転職先ではPHPを書いている。そのうちこれについては表になる情報が出てくると思うので、お待ちくださいまし。

ちなみに The Rust Programming Language 日本語版 これをやっていた。日本語版と英語版の両方を読んでいる(差分があるので、英語版を読むのが良いが、残念ながら英語版では自分の頭が足りず、誤読をしてしまうため併読)。

8月

7月中にRustの勉強を終わらせる気でいたが、気まぐれで PyCon JP のプロポーザルレビュアーをやった結果、Rustの勉強が終わらなかった。勢いに乗ってプロポーザルはすべて読んだ。これはこれで面白かったので、のちに書きたい。

ISUCONの練習会をした。ISUCON10の問題をやったけど、あまりスコアが上がらなかった。というか環境構築に時間がかかりすぎてしまって本質的なスコアを上げるのが難しかった。座標系にインデックス張ったりするやつ試しておきたかったなぁ。

GitHub CLI を使って PR を出したら、フォーク先じゃなくてフォーク元に対してPRが出てしまった。恥ずかしかった。悲しい。

github.com

8/15あたり

疲れ切って龍が如く0と、超次次元ゲイム ネプテューヌRe;Birth1 をクリアした(素晴らしい人間ではないので、気を抜いてしまう)。最高だったので、これについては後で書く。

Rustドキュメントをちょっと追い込んで、コレクション型までやった。

8/16~8/20

結構仕事を頑張っていたと思う、一応リード的な役割を果たす人になりつつあるので、いろいろなプロセスに入って行ったり、改善したりしている。このことについても今度書ければいいと思う。

Rustはちょっとやってた。あと MySQL 8.0 のコンフィグを覚えておきたい(練習会の時に MySQL 5.7 と同じコンフィグを書いたら、MySQL 8.0 では使えない定義だったりしたものもあってハマったので)と思って最後にQiitaをいくつか読んだ。

nginxのテンプレをここから拝借してまとめておいた。

SCARLET NEXUS がSteamでセールしているので買う。

知り合いが開催している yonaki.dev ににぎやかし参加する。育児関係に対するドメイン知識が増えたが、私は結婚も育児もしていないので、知識を活かす先がない(?)。ただお金がかかるイベントであるのは事実なので、そこに対するスケジュール、金銭管理、また赤ちゃんごとの特性なども加味しつつ、準備とリスクヘッジが大事ということはわかってきたので、プロジェクトマネジメントっぽい視点で俯瞰していかせることも多そうだと感じた(?)。

当日

これからは時間軸ごとに。

9:00

起床できた。ISUCON予選のYouTubeが始まるまで、これを見てた。(月一くらいで見てる気がする)

www.youtube.com

9:45

飯食べながらYouTubeを見る。チームメンバーは無事に集合していた。安心した。

10:00

競技開始。ポータルが正しく動いていることに驚く。

11:00

  • (全員)マニュアルを読む
  • mosさん)テーブルの構造などをつないでもらい、DBとアプリケーションの分割を担当する
  • (pxfncさん)git pull したらデプロイできるようにする。GitHubaccess tokenとかやってた気がする。
  • (yumechi) nginx のテンプレ設定を張り付ける。ISUCONのコンテスト中の説明を2人にしたり、しなかったりする。各種マニュアルをリンクするだけの人。マシンのIPを共有するだけの人。

12:00

  • (全員)各種ツール類のインストール、コードを読んであたりをつける
  • mosさん)アプリケーションとDBマシンの分離をしてくれていた。点数が2~3倍になる。助かる。
  • (pxfncさん)Rust周りの環境構築をローカルでできるようにしていたり、git pull でデプロイできるようにしたり、Rustをビルドして再実行するだけの小さいスクリプト(これが超便利だった)を作って開発をしやすくしてくれていた、助かる。
  • (yumechi) DBのgeneral logと slow_query log を出すようにして、ベンチを回して様子見。 general log から強く問題がありそうな n+1 を特定する。ちなみに設定をミスっているためか、一生 slow_query log が出なかった(なぜ?) この時点で slow query の問題より、 n + 1 のほうが問題があると考えてしまう。

general log から insert が多いシステムだと判断できたので、 insert 周りのケースで問題があるところと単に index 張って直せそうなところを中心に見ていく作戦にする。

自分自身の反省としてはここで where に condition が含まれてないので、 condition の分割優先順位は低いと思ってしまったこと。コードを見れば自明だったが、検索コストが高いことから分割すべきであった。

14:00

  • (全員)ご飯食べる。ご飯は大事。
  • mosさん)condition周りの改善ができそうということだったので、分割せずに条件分岐を最適化し始める。文字列パターンがせいぜい8通りなのでべた書きして高速化する。元の処理よりはかなり早くなっていた。
  • (pxfncさん)Rustのコード書くサポートしてもらいつつ、trendの改善に乗り出してもらう
  • (yumechi)雑にindexを張って点数を稼ぐ。trendがまずそうなので、コードを読みながら pxfnc さんと協力しながら進める。MariaDBのコンフィグをいじくりまわす。

ここでの反省は各マシンのCPU使用率や、メモリ使用率の確認を後回しにしてしまったこと。せめてEC2のダッシュボードくらいは眺めてもよかった。16:00過ぎにtopしてみてメモリの量とか見て、過去年度よりもISUCONのマシンスペックが高いことに気が付いた。もっと別の戦略もありそう。この時点でもう少し見ていればDB分割なども視野に入れられたかもしれない。

16:00

  • mosさん)マシンの接続周りの設定を変えてもらう。alp で各種APIリソースのコスト計算などを見てもらう。condition最適化の続き。
  • (pxfncさん)trendのコードを最適化してもらいつつ、Rustのサポート。
  • (yumechi)imageのカラム fetch をやめるために select * になっている部分をつぶす。多少効果があったっぽいがコンパイルエラーを発生させて0点をたくさん取った。コンパイルできるかは手元で確認しましょう。pxfncさんがクエリ書くのに苦戦しているので、自分が欲しそうなクエリを書いてRustの実装はお任せする。ついでにRustでキャラクターの値も const にしたがずっとコンパイルエラーを起こしていてすみません!!!!!!と言い続けていた。

時系列順で考えた時に下記を思い出す。

https://stackoverflow.com/questions/12752375/mysql-group-by-id-and-latest-datetime

IDのMAX = 時系列の最新かどうかについては非同期処理の場合必ずしもそうとは言えないのではないか、という一種賭けに近いところはあったのだが、下記のロジックでお願いした。

  • 各椅子のコンディションについて、椅子ごとのコンディションIDの最大値を求めててもらう(サブクエリで書けることはわかっていたが、これを再実行するコストのほうが高いと判断して、先に実施して id 列を出してもらう方法にした。実際一発で書いたほうが短くなるような気がするし、意味があったのかは確証がないが、感情的にサブクエリが実行されるより指定された id を in で調べた方が早いと感じた)
  • この結果を使って、椅子と椅子のコンディションを join して結果を求める

これによってかなりスコアが伸びた。凍結前 2.5 万くらいのスコアだったはず。

これは事実です。Rust書けなさすぎワロタとなっていた(じゃあ予選突破者人気のGoだったらどうだったかといわれると、Goのほうが触れないので余計無理なのだが。これに関しては追記参照)。

17:00

この辺りからモブプロになっていく。

  • 残り時間でやりつくした感じがあり、あまり触れないようにしていたが、延長の話が出てたのでもうひと挑戦したくなった
  • TODOになっているところを外してみることに気が付く。
    • 初期のころ、外してかなりスコアが悪化したことから触らないようにしていた
    • クエリ、レスポンス改善によりアクセス数が増えても良いのでは?という戦略になり、外すことにする。
    • drop_probability を 0.5 -> 0.1 -> 0.0 に変更してベンチを取り続けていたが、幣チームの場合は大きくスコアが上がった。3倍くらい。
    • 値を少しづつ変えてベンチを回していた
  • 調整中にベンチが凍結されてしまうので、作業がストップしてしまった。
  • (pxfncさんはビルド周りについて調べててくれていたはずだけど、あんまりチャットログが残ってないのと、僕のRustのエラーのサポートめっちゃやってくれてたので神でした)

この辺りで5~6万だった記憶。

18:00

  • nginxで一部のリクエストを分割できないか?という話になる
  • upstream でほげほげすればできそうというのを mos さんが調べてくれた(神では?)
  • 3台目アプリケーションサーバーにしようという提案をしてpxfncさんにセットアップしてもらう
  • yumechiがnginxの設定をいじりつつ、mosさんにチェックしてもらう
  • 設定がうまくいき、3台目がアプリケーションサーバーになる

www.nedia.ne.jp

この記事が参考になりました。

ログがうまく流れね~~とかそういう話しつつ、スコアが上がったからまあいっかとなる。これ本当に成功してよかった。

最後に nginx の微妙な設定を yumechi がいじったり、ログを全て出していないことを確認したり、再起動テスト(これ 17:00, 18:00 もやって試したので万全ではあったけど)やって終わりになりました。

ポータルで最後確認したのは 13.8 万程度。

18:45(コンテスト終了)

疲労困憊してた

19:00

www.youtube.com

クロージング見てたら、豚乙女さん?!?!!??!!?!?!??!!となって心臓が飛び出た。

19:30

集まって話していたけど、みんな疲れたのでそのまま落ちた。例年ならここで飲み会とかするんだろうけど、もうそんな元気すらなかった。

21:00

超次次元ゲイム ネプテューヌRe;Birth1 のやりこみや、 CELESTE Classic をISUCONチーム外から紹介してもらって遊ぶ。

8/22(翌日)

めちゃくちゃ良く寝ていた(11~12時間程度)。疲れていたんだと思う。

起きてから ABC 215 B - log2(N) を一生バグらせる。センスがない。一応解けたけどこういうことではない。多分この手の問題は C++ より Python の得意とするところなので、普段なら間違いなく Python で解く。

14時に結果が発表されたが、なぜかチーム名があり困惑した(うれしくもあったが)。勉強や準備が間に合わず ISUCON から提供されている Splunk や NewReric なども活用できず、素朴に素朴に点数を稼いでいっただけだったので、まさか残るとは夢にも思っていなかった。

まとめると下記4つの戦略が運よく大きくきれいにはまり、点数が線形に伸びていったことが良かったと思う。チームメンバーに感謝…。

  • マシンの分割(app2台、DB1台)にできたこと
  • trendの最適化
  • 全員が主体的に動けて、通話しながらチームワークを発揮できたこと。git pullやRustのビルドスクリプトなど素早く開発できたこと
  • 全員がマニュアルをよく読んでおり、点数になるポイントを抑えられたこと。最後のTODO外すなどの戦略はとても良かった。

決勝の発表後と気持ち

  • とりあえずISUCONの決勝の日、テスコンの決勝戦とも被っているので日程調整
    • 各所に頭を下げてきた。「どうしよう?!」と思いつつ、双方のチームの方にご理解いただいたのでよかった(本当に申し訳ないとは思っている)
    • この時間はこれで行きます、みたいな調整をした(ISUCONはフルで参加できず、時間帯が存在することになるが、テスコンもテスコンでコミットしたい部分があり、どちらも出ることで自分は決断することに)。
    • テスコンはこれから最後の追い込みなのでやっていく(ISUCONよりかなり貢献できてなくてチームメンバーにお礼することしかできとらんが…)
  • ワクチンの副反応が心配な時期なので、体調を万全にすべく準備しておかねば
  • 8月中はテスコンがんばって、9月はISUCONを頑張って、ISUCON終わったらDBスペシャリストやって、みたいな感じになりそうで、DBスペシャリスト終わったら少しゆっくりしたい(ほんとかな?)

まとめ

勉強目的での参加だったので、決勝に残ったこと自体がびっくりだったのですが、結果以上に(自分の都合やわがままに突き合わせてしまっている)一緒に活動しているメンバーに感謝をしています。ありがとうございます。

素朴にやった戦略がたまたまはまって点数が出たので、非常にうれしいですし、やってよかったと思います。

本番に向けてですが、復習として下記が思い浮かんでいます。すべては実施できないかと思いますが、いくつかには手を付けたいです。

  • Rustの勉強(言語が書けなさ過ぎて足引っ張ってしまったので)
  • テーブルカラムの設計(変更してダンプして、編集する、みたいなところは今回避けてしまったが、シュッとやれていれば~~と思っている)
  • 他の分析ツールとか、手法。全然知らなさすぎる。
  • コンフィグ周りをシュッとあてる方法。ansibleでやれたらな~~~と思いながらできなかった。config自体は用意できていてよかったが。
  • MySQL8勉強するか、MariaDB勉強するか。自分の知識はMySQL5.7に対してのものが多く、学習コストだとMariaDBやったほうが効率がいいような気がしている(MySQL8に対してのdiffや勘所が頭に入っておらず、MariaDBのほうが自分の直感に近いのではないかという感覚がなんとなく存在している、本当か?)
  • キャッシュ周りやオンメモリ戦略(Condition以外はメモリやキャッシュに乗せられる形のような気がしていて、うまく乗せて高速化できた可能性がある。決勝でもそのような考え方ができるシーンはあるかもしれない)
    • generated column も一瞬使ったけど、やめた。timestamp のソートには使えたかも。
  • コンフィグ周り、知識が何も足りていない
    • 雑に gzip 配信していたが、必要なかったらしい
  • インデックス設計。uuid, timestampについては即座に張ったが、複合インデックス戦略とか、-1をかけた何かを使う(ISUCON10でもあったテクだと思うが)などもあり得るかもしれない
  • DB2台戦略。DBのCPU張り津きしているのは知っていたが、APPのほうが課題間合ったので、そっちを優先してしまった。コンフィグと合わせて調査が必要。
  • Varnish、何(ISUCON感想戦で見たが、知らないもの)
  • SQLで解決できる問題はSQLで解決しましょうということ(limit 周り見直せるところは結構あった)
  • 分析方法の最適化。generalログを目でgrepするのは効率が良くないし、動的にカラムが変わるようなクエリが投げられた場合、集計がうまくいかないはず。この辺りは調べておきたい。
  • alp自分も使えるようになりたい
  • ISUCONの本を読む 3章あたりまでしか読めてない
  • 去年の自分の参加記を読み返す

追記

追記1

Rustの知見が世の中に出始めているのにもかかわらず、Rustの採用数が少ないことに驚く。決勝進出チームはわずか2チームなの意外に思う(自分はPythonPHPしか書く方は得意ではないので)。自分たち自身もRustの強みを強く生かしきれたわけではなかったが、もっと多いと思っていた。

ちなみに前職でも同じ部署だったメンバーからRustおすすめされていたこともあり、今年はRust勉強する気でいくぞ~と思っていた。

本番でいろいろやらかして恥ずかしかったのですが、コンパイルエラーめっちゃわかりやすくて体験としては良かったので、もう少し向き合ってみるかも。言語の良しあしについては何とも言い難いものの、今回の予選で自分の感覚ではこれからフィットしてくる言語かもしれないと感じました。

追記2

最終的に5桁→6桁に盛れたのでよかった! 伏線回収完了。(この発言はISUCON11 予選オープニングトークにて参加者数の桁が間違っていたことにかかっています)

追記3

自分のスキルセットとかを参考までに。

  • ISUCONに関連する言語言語
    • Pythonを仕事で3年半くらい、自然に読み書き可能
    • PHPを仕事で2か月くらい、読み書きはそれなり
    • Go, Ruby, Rust, Nodejs は読めはするけど、その言語特性に合った書き方は全くできてないと感じる
    • Perlコードゴルフ以外で読んだことない
  • ミドルウェア
    • Nginxのテンプレコンフィグを埋めるくらい
    • MySQL5.7のデフォルト設定をいくつかいじるくらい
    • 他はほぼ知らない、Redisはちょっと触ったけど、仕事でやったことはない
    • 一般的な設定までで理解が止まっていて、細かい設定勝負になると絶対に負ける
  • チューニング
    • インデックス設計は感覚レベルでしかできない
      • 最適なものを張ることというより一時しのぎだが、効果が出そうなものをやるくらいしかできてない気がする
    • クエリのフェッチや実行回数に対しての肌感がなんとなく存在しているような気がする
      • いわゆる論理的にというより、ニオイで判断するタイプなんだろうという自覚が強い
      • 今回はたまたまこれが完全にハマっただけで、運が良かったと思っている

自分が初回(4年前)に参加したときはこんな感じだったなというのを思い出す。これに比べたら、確実に成長している。ただし、まだまだ足りないところばかりである。

  • ISUCONに関連する言語言語
    • Pythonを仕事で半年くらい、読み書きはそれなり
    • PHP, Ruby, Nodejs は読めはするけど、その言語特性に合った書き方は全くできてないと感じる
    • Perlコードゴルフ以外で読んだことない
    • Rust, Goは知らない、何も理解していないので読み書きすらできない
  • ミドルウェア   なんもできないし、本当に何も知らなかったし、今思うとなぜこれで働けていたのかが正直謎になっている   ひよっこだったなあと改めて思うし、当時のメンバーはやりづらかったんじゃなかろうか…
  • チューニング
    • なんとなくインデックスを張るくらいはできるだけで、なぜインデックスをそこに張るのかの根拠がほぼなかった

追記4

去年やるといってできたこととしてはこれくらい。

  • Generated Columns への理解。結局使わなかったが、その場で作ってみるとかはしてていた。MySQLのGenerated Columnsまとめ with Rails を読んだ
  • この nginx 設定を実践した
  • nginx 周りのあるある設定は押さえた、まだ学ぶことは多い
  • CPUやメモリ使用率周りは見ることができた、見てから何も改善していないが
  • ansibleやると思ってちょっとやったが、実践投入は無理そうだった。ISUCON10の予選リポジトリとか参考にして勉強し直したい。

できなかったことはこれら。去年調べたツール系ほとんど調べてないじゃないですか。

  • Rustの勉強が不十分
  • https://namonakimichi.hatenablog.com/entry/2020/09/20/205906 で書いたことはほぼ調べてない
  • MySQL 8.0 周りはほぼ理解できてない、まずは差分を追わないと
  • 監視系、結局今年もできてない。メトリクスを見るのが弱いという自分の性質も相まって後回しになっている。よくないので、決勝までにできそうなことを探す。

追記5

チームメンバーとの分担とかを書いてない気がしたんですが、結構モブ的にやっていて明確に役割が分かれていたのは前半だけで、後半15時くらいからはモブプロっぽく画面を共有して議論する形が多かったように思います。結果的に戦略がハマったこともあり、いい感じでした。

追記6

そのうち予選で使ったリポジトリを公開するので、公開したら追記します。(管理は pxfnc さんにお任せですが)

公開されたので貼っておきます。

github.com