私が歌川です

@utgwkk が書いている

ISUCON9に参加した #isucon

ISUCON9に参加しました。チーム名は :innocent: で、チームメンバーは id:wass80id:nonylene です。去年と同じチームで、id:nonyleneにインフラ周りを全て見てもらいながら私と id:wass80 で実装を見るという作戦でした。使用言語はPythonです。

最高得点は5410点、最終得点は0点で、予選は突破できませんでした。

nonylene.hatenablog.jp

github.com

やったこと

各言語実装のディレクトリにgitignoreを配置する

gist.github.com

唯一前日から用意していたこととして、こういったスクリプトを書いていました。 これを実行すると各言語実装のディレクトリに対応するgitignoreを置くことができるので、リポジトリが肥大化するのを防ぐことができます。

categoriesをベタ書きする

categories は固定なので、毎回DBから引くのではなくアプリにベタ書きしました。 当初は /initialize でDBから引いたものを使いまわすようにしていたけど、 なぜか flask.g に書き込んでも読み込めなくて断念しました*1*2。 デバッグ出力からdictをコピーしてきたけど空白が入るなどでけっこうバグってしまい、もうちょっとスムーズにやりたかった……。

GET /new_items.json のN+1を潰す

item_simples に対して get_user_simple_by_id を回していて明らなかN+1だったので潰しました。 今回はINNER JOINじゃなくてSELECT ... WHERE id IN (...)を発行していたのですが、ロジックはこっちのほうがわかりやすい気がします。

GET /users/transactions.json のN+1を潰す

GET /new_items.json とだいたい同じですが、 transaction_evidencesshippings はLEFT JOINで引っ張るようにしました。

インデックスを貼る

pt-query-digestの結果を見つつ次の2つのインデックスを貼りました。

create index items_seller_id on items (seller_id);
create index items_created on items (created_at);

やらなかったこと

商品の表示順の調整

当日マニュアルを見ると、商品一覧の表示順はユーザの編集が反映されてさえいればなんでもよいように見えたので、じゃあ高価な商品をトップに持っていけば点数が出るのでは? ということは検討していたけど、結局やらなかったです。やってもよかったけど競技終了ギリギリになってしまった……。

外部APIの解析

外部APIが2つあって、どちらもHTTPSかつHTTP2でレスポンスが返ってきていたので、これをうまく使えないか? とか、どんなレスポンスヘッダがあるかとか実はキャッシュできるのでは? とか今になって考えているのですが、そこまで手が回りませんでした。 GET /users/transactions.json でN回外部リクエストを送っているのが明らかにやばくて、これはチームメンバーに改善を託していました*3*4。でももうちょっと気合を出してじぶんも実装を見たほうがよかったな……。

得られた知見

VSCode Remote Developmentは重い

最初はVSCodeでリモート編集をしていたのですが、htopを見たところVSCodeのサーバがめっちゃメモリを食っていたので、落としたところ点数がかなり増えました。 それ以降はずっとリモートのvimでコードを書いていました。

画面が多いとお得

当日は id:wass80 が東京にいたので、Slack callとzoomを駆使して音声や画面を共有していました。 チャットで書くよりも音声のほうがやっぱり高速に意思伝達できるし、画面共有でペアプロやペアオペが可能なのはすごく便利でした。

非同期処理がやりたくなったら非同期処理が得意な言語に逃がすのがよい

非同期処理の実装はずっと眺めているだけだったのですが、Pythonで非同期処理をスッと書くのは難しそうでした。 Node.jsに逃がす作戦も検討されていたのですがこれはふつうに時間が足りなかった気がする。

当日マニュアルを読み飛ばさない

自然と書き忘れてた、これが一番大事!!!!!!!!!!

3人いて誰も複数台構成にしてよいことに気づかず、あ~今回はサーバ1台か~って言いながら競技終了まで実装をしていました。こんなことある?

謎だったこと

gunicornやuwsgi経由で起動するより python app.py ってやったほうがスコアが出た

これはマジで何だったんだろうか……。しっかり調査したい。

感想

年々どんどん参加者のレベルが上がり、そして問題のテーマもおもしろくなっています。 N+1を潰してインデックスを貼れば学生なら予選突破できる時代は終わり、アプリケーションの本質的な改善に手をつけないといけない時代になっているのですが、身体が追いついていないですね。

毎年こんなに面白いテーマの問題が出てくるのすごいなあと思っています。

*1:追記: これたぶん /initialize を受けたワーカースレッドにしか flask.g.categories がsetされていなかったからでは? と思う

*2:追記: それはそうとしてベタ書きにするのはRedis入れて云々~よりも引っかかりが少ないと思うのでよかったのではないか、結局バグらせはしたが……

*3:なぜなら前半ずっと実装しつづけていて疲労していたので

*4:なぜなら頭は1つしかなく、非同期処理がよくわかっていなかったので