ISUCON9予選2日目2位(全体5位)で突破しました。 チームメイトは前回と同じくmethane、mapk0yで、前回の予選敗退のリベンジでした。

本戦に向けての反省として、やったことやらなかったことをまとめます。

やったこと

事前準備

作業用サーバを用意し、デプロイコマンドや各種メトリクスを回収、一覧する仕組みを準備しました。 といっても前回も使ったもので、ベースはmethaneが優勝したときのツール群を引き継いだものです。 これらの仕組みのため、使う言語はGo一択でした。 また、使い方をすっかり忘れてしまっていたので、過去問で一度リハーサルをしておいて本当によかったです。

役割分担として、インフラをmapk0yにまかせ、methaneはアプリのコードから、 僕はスキーマやクエリからチューニングポイントを探していくことにしました。

午前中

まずはmapk0yに環境を作ってもらっている間に、二人でレギュレーションをよく読みました。 この時点でcampaignが変更できることや、パスワードに関する記述が曖昧なのでハッシュ関数を 差し替えできそうなことに気づいていました。

環境ができた時点で事前準備していたpprofなどを仕込むコードを投入、ベンチを回して様子をみました。 また、campaiginを変更してみてどう変わるか試したりしました。

チューニングに取り掛かり、myprofilerで上位に上がってくるcategoriesを真っ先にオンメモリ化しました。

12:00~14:00くらい

pprofなどを見ながら、getUserSimpleByIDのN+1の解消に取り組みました。 最初からsqlxが使われていて、導入する手間が省けたのは良いのですが、 そもそもsqlxについてあまり分かってなかったので、使い方を勘違いしたりして手間取ってしまいました。

僕が苦戦している間にloginを空いている2台に逃したり、DB上でのロック競合が軽減されていたり、 スコアも伸びて暫定1位になっていました。

14:00~16:00くらい

この頃には外部API呼び出しがボトルネックの中心になっていました。 mapk0yがchoconの導入を提案してくれたので(出題者がメルカリなので)、 chocon経由でAPI呼び出すようにアプリ側を修正し、ついでにconfigテーブルもキャッシュしました。 外部APIがBadGatewayとかをちょこちょこ返すので、APIリクエスト元を3台で分散させたり いろいろ試行錯誤していました。

このとき、2日目1位のチームnilがどんどんスコアを伸ばしていて、少しだけ焦りもちらついてきました。

16:00~終了

外部APIのエラーはどうやっても防げないと諦め、何度かリトライするようにしました。 他には、もういちどDB周りを見直してexplainを叩きながらindexを追加、campaignの値の調整などをしていました。

残り1時間時点で2位で1位が倍ほどのスコアを出していて不安もありましたが、 前回の予選では時間ギリギリに大きな変更を入れてスコアが下落、敗退してしまった反省もあり、 早々に改修をやめて再起動試験をし、お祈りの時間としました。 これまでの経験から、終盤ベンチが不安定になってスコアが下がることがあることを予想していました。

台風も近づいていたので競技時間中でしたが後片付けを済ませ、ポータルトラブルで競技時間が延長されてもいましたが、 18:00には3人ともすでに帰路についていました。

やらなかったこと

話題になっていたbcryptについては、予選では飛び道具は使わずに突破しようということになり、 分散するだけで実装には手を付けませんでした。 ざっくり役割分担していたこともあり、インフラ周りはmapk0yに全面的に頼って僕はなにもしませんでした。 というか頼もしすぎて手も足も出せませんでした。

また、メトリクスの一覧ばかり見ていて、結局チームメンバーの誰も アプリの画面を一度もちゃんと見ていませんでした。こんなんで良いのだろうか。

やれなかったこと

sqlxの使い方を勘違いしてしまったりでN+1解消手間取っただけでなく、 transaction_evidencesとshippingsテーブルのN+1がまだあったことに競技が終わってから気づきました。 そして、競技時間中チームメイトがやっていることをあまり理解できておらず、 しっかり議論に加われていなかったのも反省点です。

まとめ

いろいろ騒動にもなっていましたが、講評や他チームの解法を後からみればみるほど、 問題として本当によくできていたなと感心しています。

本戦もがんばります。