ISUCON14にチームOL001として参加しました
まえがき
ISUCON14にチームOL001のアプリケーション担当として参加してきました!
最高スコアは9837点でした!詰まりポイントがいっぱいあって思ったようにスコアを伸ばせませんでした。
(最終スコアはおそらくfailしている)
当日までの練習と本番の様子をまとめていきます。
図: スコア推移。ベンチが通らず苦労している時間がかなり長い。
チームメンバー
去年と同じチームメンバーで2回目の参加になりました。
以下チームメンバーと担当です
名前 | 役割 | ISUCON参加回数 |
---|---|---|
@noko | アプリケーション担当1 | 3回目 |
@yshir | アプリケーション担当2 | 2回目 |
@takeokunn | インフラ担当 | 2回目 |
当日までの練習
去年は初参加のメンバーがいたことから、およそ1ヶ月半前よりほぼ毎日集まって2時間ほど練習していました。
しかし、負担が大きかったことから、今年はまず1ヶ月前に思い出しを2時間と通し練習を2回以外は個人に完全に任せることにしました。
おかげで精神的にはかなり楽になりましたが、あまり練習できなかったことから本番で思ったように手が動かなかったという結果に終わったので、バランスが難しいです。
思い出しと個人練習には private-isu を、通し練習には isucon9q と isucon13q をやらせていただきました。
通し練習では両方ともいい感じにスコアを伸ばせたので完全に慢心していました。
使ったツール類
去年とほぼ変わらずでした。
- プロビジョニング/デプロイ
- ansible
- ベンチ前のデプロイをansible経由でやると遅くて良くないので、簡単な変更はscript一発で流せるようにしていました
- ボトルネック測定
- alp
- pt-query-digest
- pprof
- db負荷が支配的だったので使わず
- 結果まとめ
- github issue
- slack
- 細かい情報共有
当日のタイムテーブル
今回もオンライン参加です。
meetを繋いで適宜画面共有をして進めていました。
ベンチの結果(点数、alpの結果、pt-query-digestの結果)は github issue に半自動でまとまるようになっています。
9:30 起床
今年も無事全員起床できました。えらい。
10:00 最速ベンチ狙い
最速でベンチを回せるように isuconポータル と AWSのCFnデプロイ前の画面で待機していました。
ベンチ結果は2番でちょっと悔しかったです。
ベンチ回すのと平行して @takeokunn にプロビジョニングをお願いしました。
裏で動いている isuride-matcher
がログを溜めまくって alp がうまく動いていなかったところ以外は詰まりどころもなく、順調にセットアップできていた印象です。
プロビジョニングが完了するまではひたすらマニュアルを読み漁り、キャッシュできそうなところとか肝になりそうな仕様を抜き出してslackに貼る作業を行いました。
45分くらいはマニュアルを読んでいました。ボリュームがすごい。
10:45 適当にできる改善をする 3000点くらい
@yshir がSELECT しているが index が貼られていないところに適当にindexを貼ってくれました。
自分は admin prepare の設定を適当に入れた状態でとりあえずベンチを回したところ瞬間1位になって喜んでいました。
11:30 slow query の一番上にあったものを修正 4000点くらい
GET /api/owner/chairs
にあった総走行距離とその更新日時のクエリが平均1sとかなり遅かったので潰しにいきました。
↓なんか大変そう
SELECT id,
owner_id,
name,
access_token,
model,
is_active,
created_at,
updated_at,
IFNULL(total_distance, 0) AS total_distance,
total_distance_updated_at
FROM chairs
LEFT JOIN (SELECT chair_id,
SUM(IFNULL(distance, 0)) AS total_distance,
MAX(created_at) AS total_distance_updated_at
FROM (SELECT chair_id,
created_at,
ABS(latitude - LAG(latitude) OVER (PARTITION BY chair_id ORDER BY created_at)) +
ABS(longitude - LAG(longitude) OVER (PARTITION BY chair_id ORDER BY created_at)) AS distance
FROM chair_locations) tmp
GROUP BY chair_id) distance_table ON distance_table.chair_id = chairs.id
WHERE owner_id = '01JEHYZMJRWT1SZSF3D4B9ZXQT'\G
やっていることはかなり単純で、走行距離を計算するのと、最終更新日時をとっているだけです。
わざわざここで計算する必要性を感じなかったので chairs
に カラムを新しく生やし、 chair_locations
への insert があったら trigger で値を埋めるようにしました。
なかなかベンチが通らず苦労していたんですが、 inisital data の insert 時に chair_locations
-> chairs
の順番で値埋めが行われるので正しく初期値が埋まらないという単純なものでした。結局 3-initial-data.sql
を変更して対応しました。sqlファイルのサイズがそこまで大きくなくて助かりました。
12:00 細かい修正 5000点くらい
カバーしきれていなかった index を追加してちょっとだけ点数を重ねました。
12:30 notificationへの対応
このあたりから alp でアクセス回数が目立っていた GET /api/app/notification/
と GET /api/chair/notification
の改善に手をつけ始めました。なおここから最後までずっとこのエンドポイントと戦い続けることになります...。
マニュアルにも記載があったので、やったことはないですがSSEに対応することにしました。
まずは時間ベースで定期的に値を返すようにしました。3時間くらいかけてなんとか動くところまで来たので、statusの変更ごとにeventを返すように変更を始めました。
が、なかなかうまくいかないので一旦諦めて他の改善を探しに行きます。
15:30 notification の中身のクエリ
イスの評価を取得するところがN+1になっていて遅そうだったので 総走行距離と同じように trigger で chairs
に直接保存するようにしました。
が、点数が下がったので結局マージしていません。
16:00 notificationへの対応 再び
より効率的に notification を返すようにするために、 ride の status が更新されるたびにeventを返すようにしました。
が、これは結局動いていません。最初の1回は値が返るのですが、それ以降のeventが通知されず、終わったあとも1時間くらい戦いましたがよくわからないまま終わってしまいました。
17:00 notification をキャッシュする
SSEがうまくいかなかったので結果をまるごとキャッシュするようにしました。これもベンチが通らなかったのでマージはしていません。
17:30 すべてを諦める
これ以上やっても間に合わなさそうだと思い、一旦手を止めました。
あとはサーバー分割を @takeokunn にお願いしてスコアを上がることを祈っていました。
しかしサーバー分割ではスコアがうまいこと伸びず、さらにいままでなかったfailが見え始めて、そのまま回復せずに終わってしまいました。
来年に向けて
知識レベルがほぼ去年のままで挑むことになったのが良くなかった点だと思いました。練習ではほぼ手なりでできるところまでしかやらず、コンフォートゾーンを抜けられていない感じがあります。
もう無理!ってところまで点数を上げたあとに時間をかけてもいいから更に点数を伸ばす努力をするのが今の自分には効きそうだなあという印象です。
あとは本番でやったことないことをやろうとしたのは、経験としては良かったんですが、本番としてはだめでした。もっと手なりでできる形を増やして余裕を持って挑めるようにしておくのが大事です。
来年はしっかり練習して臨みたいと思います。
あとがき
今年はかなり悔しい結果に終わってしまいました。
しっかり復習をして、来年は良い結果が残せるように修行をします。
Discussion