私が歌川です

@utgwkk が書いている

ISUCON11予選落ち #isucon

チーム :innocent::innocent::innocent: *1 で出場しました。メンバーは去年と同じく id:nonyleneid:wass80 です。最終スコアは13369点でした。最後のベンチマークでは12000点ぐらいを記録しました。

isucon.net

やったこと

今年もGo言語で出場しました。他のメンバーがやったことはそのうちブログ記事が出るはずなので、あとで参照します。細かい実装などはリポジトリを参照してください。

github.com

splunk導入

splunkでトレーシングを行えるようにするために ctx を引き回したり、トレーシング用のライブラリを導入したりしました。練習していたので去年ほどは手こずらなかったです。

pprof導入

echoにpprofを導入しました。echo v4用にフォークされたechopprofを探して使えたので、これも去年ほど時間はかかりませんでした。

アイコン画像の静的ファイル化+etagによるキャッシュ

アイコン画像をDBから読み出しているのを見かけたので、ファイルに書き出して参照するようにしました。ログインユーザーのISUじゃない場合は404を返す実装になっていたので、nginxから返すようにするのは断念しました。nginxの X-Accel-Redirect ヘッダは聞いたことはあったけどこういう場合に使えるのか、と他チームの参加記を読んで感心しました。

そのあとetagを返して If-None-Match ヘッダを見て304を返す実装を入れました。

インデックスを貼る (失敗)

いつものようにアプリケーションから発行されるクエリを眺めて、コンソールで CREATE INDEX して回りました。が、初期化時にテーブルを丸ごと作り直していることに競技終了まで気づいていませんでした。これは私が悪いので石を投げてください。

isu_condition (jia_isu_uuid, `timestamp`) とかに効かせたらMySQLだけでも多少マシになったはずなのでめちゃくちゃ悔しい……。そしてチームのScrapboxを見返したら全く同じインデックスを2回貼ってるのに気づきました。これは私が悪いので石を投げてください (再掲)。

isu_condition テーブルをInfluxDBに載せ替える (断念)

isu_condition テーブルは明らかな時系列データなのでInfluxDBに載せ替えよう、という提案にゴーを出して、高速にInfluxDBに入門しました。InfluxQLを使うとほとんどSQLみたいな書き味で、複雑なことをしないクエリはすぐに移植できました。

しかし後述するようなさまざまな苦難が襲いかかり、最終的には載せ替えるのを断念して、ブランチを切り替えました。

データ不整合に苦しめられる

ISUのコンディションをDBから取得する実装をInfluxDBに移植してもらったところで、データ不整合でベンチマークがfailしまくるようになりました。登録されるはずのISUが登録されていないのではないか、InfluxDBにbatch insertする箇所が呼ばれていないけどなぜなのか、といろいろな箇所にログ出力を差し込んでいましたがなかなか原因は分かりませんでした。

原因が分かったのはなんと2時間後で、初期化時にInfluxDBに isu_condition テーブルの初期データをinsertするようにしたら直りました。有史以来データ不整合がずっと起こっていたというわけです。これに気づくのにこんなに時間がかかったのはマジで悔しい……。

/api/trend のInfluxDB化がうまくいかず

ついに isu_condition を参照している箇所を全てInfluxDB化できましたが、ベンチマークを走らせるとまたもやデータ不整合でfailしました。なぜかtimestampが0になっているので明示的にInfluxDBに記録する、アプリケーションでソートする、などを高速にやりましたがデータ不整合を直せず、実装を差し戻すことになりました。

POST /api/condition/:jia_isu_uuid でbulk insertする

競技終了ギリギリにInfluxDBに載せ替えるのを断念して、MySQLを使う実装に戻して最後にできることをやろう、ということでbulk insertを実装しました。commit logを見たら18:41*2に実装完了してて、しかも一発でpassしててすごすぎる。

placeholderを文字列結合で増やしていましたが、こうしなくてもsqlxの NamedExec を使うとbulk insertできるというのをあとで教えてもらいました。

github.com

反省

必殺技に全てを振ってしまった

8時間の大半を必殺技に振って、最終的に差し戻したのが何よりも悔やまれます。たしかにInfluxDBの得意領域であろうとは思いましたが、知見がなく見通しのつきにくい道に全てを振ってしまったな、と思い返しています。ある程度のデータの遅延が許される箇所はアプリケーションやRedisなどでキャッシュするとか、もうちょっと負荷を緩和する策は取れたはずです。

とはいえとはいえ、カチッとハマったらかなりMySQLの負荷を下げられて点数を伸ばせたのではないかと思っており、もっと時間・腕力・知見・などなどがあればな~~という気持ちもあります。

インデックス

私に石を投げてください。初期化リクエストで何が行われているかをちゃんと読んだらこんなことにはならなかったはずなので、次回のトライは「POST /initialize の処理を熟読する」です。

プロファイラを活用できなかった

splunkやpprofは最初に眺めたきり、MySQLのスローログは取ってもらったけどInfluxDB化に振りたかったので流し読みしてしまい、alpやkataribeでのアクセスログの解析はやっていない、ということでプロファイラを全く活用できていませんでした。

感想

予選でここまで洗練されたクオリティの問題が出てくるのは本当にすごいです。出場するたびにどんどん洗練されていく……。ポータルやベンチマークにもほとんど不自由なく競技に集中できました。

CloudFormationでインスタンスを構築するのも便利でよかったです。手でポチポチ設定して3台立てる、だと操作ミスでうまく動かないこともあると思うけど、YAMLをアップロードしたら待つだけでよくて快適でした。

あと、問題紹介の動画がよかったです。

今回は自分のミス (判断でも実装でも) がかなり目立ってしまったので、次回のトライに「深呼吸する」を追加しようと思います。

*1:triple innocentと読むことが多かった

*2:競技終了4分前