ISUCONに今年で4年目で、id:tkzwtksとid:side_tanaと「ミッシングマグネティックストレージ」チームで出て、5110点で予選敗退した。
Nodeを選んだ
例年Perlで出てるけど、今年はNodeで出てみた。その理由は
- Perlでは非同期処理が苦手なので、出せる技の選択肢を増やしておきたかった
- VSCodeの支援がよくできていたり、Chromeのデバッガにattachしてデバッグできたりと、プログラミング環境が充実している
- 定義に飛べたりとか
- 存在しない変数を参照して怒られるといった凡ミスを防げる
- メンバー全員が得意な言語がなかったので、全員そこそこ書けそうな言語を選んだ
ふだんからフロントエンドはTypeScriptで書いてるので調子よく実装できた。await書かなければ待たずに次に進むとか、Promise.allで、これとこれが終わるのを待つ、とか、こういうことをやりたいというときにすっと書けるのが便利だったと思う。
練習
週末に一度会社に集まって練習していた。僕は手元で動かしたり、デバッガを使う練習をしたり、id:side_tanaが分散トレーシングの準備(ISUCON9 予選で Cloud Trace 使った - 恋しい日々)をしたり、id:tkzwtksはAlibabaのログサービスを調べたりしていた。例年同じ技で出るよりかは、毎年新しいツールとか武器を獲得して出られる方が面白いと思う。
チームのScrapboxに知見をまとめたり、他チームにも有用そうなことは、社のエンジニアのScrapboxに書いたりしていた。
午前中
- サーバーが'なかなか立たず、問題のレギュレーションから予想される問題の特性を予想したりしていた。実際の問題を見たときに、どのパターンに当てはまるかなという感じで考えられてよかった。
- コードをGitHubにpushしようとするも、public/以下に画像が2万枚入ってたり、初期データが120MBくらいあったりしたので、一部だけGitHubに載せて、欲しいデータは手元にscpすすことにした
- ベンチマークを取ると、Stackdriver Traceに、レスポンスタイムの分布とか、その中でのクエリやAPI呼び出しが可視化されて感動した
- ちょっとした小手先の改善をしたらレスポンしタイムの分布が目に見えて変わって、これが見えてるのは勝てそうという手応えがあった
- transactions.jsonでAPIを叩きまくっている→DBから引くようにした
- DBからのN+1の解消
- categoryはベンチマーク中増えないのでオンメモリに
- MySQLとのコネクションプールを使うように
- nodeのプロセスをCPU数だけforkするように→なぜか18プロセスくらい?立ってしまって不思議だった
そうこうしてたら午後になり、焼肉弁当を食べた。前日の夜にも、どこの出前を取るかに一番時間を使っていた。
ISUCON準備してる https://t.co/nRbZSOFQ4c pic.twitter.com/uEYEHgD6sy
— 趣味はマリンスポーツです (@hitode909) 2019年9月6日
午後
途中までは調子良かったけど後半でつっかえて、そのまま時間切れという感じだった。
- buyへのPOSTで時間がかかっていて、トランザクション中でAPI呼び出ししているのをなんとかしたい、ということになった。最後までこれはどうにもできず、3人で数時間くらい使って終了
- SELECT FOR UPDATEやめてみる→効果なし
- トランザクションを2つに分けてみる→めっちゃいい案にみえたけど効果なし
- SELECT FOR UPDATE NOWAITとかですぐにあきらめてほしかったけど、MySQL5.7なのでうまくいかず
- いま考えればnode側でタイムアウトしてもよかった、awaitせず、setTimeout(..., 100)とかで結果を得られなかったら打ち切っても良かったのかも
- キャンペーンの数値を変えてみる→タイムアウトするようになったのでやめた
- キャンペーン中はタイムアウトが厳しくなるのか?とか話してたけど深追いできていなかった
- 小手先の技で新着商品を調整した
- 売れたものは出さない→これでちょっと上がった
- 一度に返すアイテム数を変える→ベンチマークが落ちる
- 高い順に返す→ベンチマークが落ちる
- API2つ叩いているところをPromise.allで並列化→スコアに変化なし
- サーバー3台構成を試す→sessionの問題?が出てやめた
- 今思うとkeepaliveの設定したら良かったのか?
- loginが遅い問題→既存のユーザーのハッシュは変えられないので困ったねというところで手を出せず
- サーバー2台構成を試していたけど、動かないまま残り10分になったので戻す
6720点がベストで、だいたい5000点くらいをふらふらしていた。
良かったこと
- 分散トレーシングを入れたり、今年はコンソールに入れたのでコンソールからログを集計できたりと、新たな技を出せたのは良かった
- 僕はコードを書くのは得意というロールで、小手先の実装をささっとできたのはよかった。エディタの環境もよくて、心地よく書いてスコアアップできたのでその瞬間はやったぜという感じだった
- データを第一に扱って、事実と仮説を分けて扱えた。ホワイトボードに仮説コーナーを作り、思い込みで進んでしまわないようにした
悪かったこと
- ボトルネックに集中することと、ペアプロに重きをおいたので1:2で動く作戦でやっていたけど、視野が狭かった。ぶつかったボトルネックの直し方がわからないときに、一人引いた視点で見る動きをやってもらえばよかった
- 分散トレースで、1リクエスト内での動きは手にとるように分かったけど、どのユーザーはいつログインして、とか、どの商品がいつどうなって、のような、リクエスト間を超えたストーリーをつかめていなかった。去年本戦で強かった人は、ベンチマークの挙動のメンタルモデルができていたと思う。変なロックを取っているのはなぜなのか、というときに、どのようなストーリーでロックがられているのかという議論ができなかった
- 最終的に3台使わないことには勝てないと思われるので、全員でアプリケーションの改善をするまえに、足回りをしっかり整えて、サーバー3台構成にできるよう準備してもらったらよかった。API呼び出しが遅いからサーバーが増えても仕方ないのでは?とか話していたけど、CPUは使い切っていたので、分散すればもうすこしスコアを伸ばせた可能性はあったのではないか
- 大きな作戦を立てて全員で集中しましょうという作戦で、連携しながら動いた。大体の場合はよかったけど、状況に合わせて、作戦のパターンを増やしたほうがよい瞬間には自立した人間が3人に分散できるようなチームをとれるとより強くなれそう
ホワイトボードのコーナー
カウボーイが暴れると言うよりは、助け合い、協調を重んじる作戦。
開始前に、自分たちが出題するとしたらどういう問題や罠を仕込むだろう、という話をしていた。API遅い説、トランザクション内API POST説、あたりは正解で、高いものだけ売って、安いものの購入は拒否すればよい、というのはハズレだった関連図は頭に入ってないと議論にならないので、最初に書く。大きなテーブルはどれなのかというコーナー。今回はデータ量はたいしたことなかった。なぜ物が売れないのかという議論。API呼び出しをなんとかしたいね〜という話。終わってから、ShipしてからPaymentを呼んでるのがおかしいのでは?とか話していた。
毎年、どこをどうしたか覚えていて、それだけ印象に残ってるということは一年で数回あるかないかくらいの楽しい一日だと思う。出題者の皆様、運営の皆様、お疲れさまでした。来年は本戦行きたい。