#isucon 2014予選に参加した

昨年 に引き続き、今年も ISUCON に参加しています。ありがたいことに今回もLINE選抜チームに入れていただいたのですが 共催枠なので本選には出られる、とかはともかく、予選に参加させていただきました。


詳しくはこちらをどうぞ

自分がやったこと

インフラも詳しいしコードも書ける2人とは違って自分はコード読み書きくらいしか出来ることないので、今回もそこらへんをやらせていただくかんじで。
ミドルウェアの導入やチューニングなどはすべて@kazeburoさんに信頼してお任せするかんじでコード変更に専念できたのは本当にありがたいことです。

Perlコード変更

問題の把握から変更方針については早めの段階で協議して決めて、まぁmemcachedにデータ突っ込んでlogin_logテーブルを消し去っていこう、という方針で徐々にコードに変更を入れていきました。

まずは「インデントを2スペから4スペに変更」と「subのあとのセミコロン除去」w

Cache::Memcached::Fastを使ってログイン失敗時に失敗回数をインクリメント、成功時にそれらをリセットするコードを入れ、それが閾値を越えているか否かの判定を使ってログイン判定をできるように。数行の変更で切り戻せるようには注意しつつ、まずはこれで問題なくベンチが走るか確認。

次に、ベンチマーク最後の/reportを出力するための部分。banned_ipslocked_users閾値越えた時点で別のところに吐き出すようにしておけばlogin_logへのINSERTが必要なくなるはず、ということでファイルへの書き出し /reportではそれを読み込んで出力するだけ、というコードに。
@tagomorisさんと打ち合わせてinit時に既存のログデータから初期状態の内容を書き込む処理はモリスさんにやっていただき、Webアプリ側は自分が担当、とキレイに分担できていてやりやすかったです。

で、実装終わってベンチマーク走らせてみたところ、Fail。どうもbanned_ipsとlocked_usersが正しい結果になっていない模様。

調べてみたら出力された内容に重複があることが発覚。既にbanned/lockedされている場合はリストに書き出さないように修正したところ 今度は件数が足りなくてFailになる。ここで暫くハマって時間をだいぶ使ってしまった…。
あーだこーだ言いながらちょいちょい試行錯誤してみたもののなかなか解決せず、結局SQLでのクエリを頭の中で考えて照らし合わせて「あ、こういうことか」と解決。単純に「既にbanned/lockedされている場合にはリストに追加しない」という分岐にしていると既にlockedされているユーザの失敗IP banやbannedされているIPからの失敗でのユーザlockが正常に判定できなくなっていた、という話でした。二つの要素を分けて考えてしまっていたのが良くなかった。

で、ようやく解決してreportも正しくなったので晴れてlogin_logへのINSERTを除去。したら今度はlast_loginが取れてなくて失敗することが発覚。そうかそういうのもあったかw とそこで気付く。これも半ば無理矢理ながらもmemcachedを使って記録しておくようにガーっと変更して解決。

ここまでやったところで期待したほどまではスコア上がらなくてちょっと残念だったものの まぁやれることはやったしこれ以上は自分は出来ることないな… ということであとは@tagomorisさんのインメモリアプリ化や@kazeburoさんのラスト調整などをぼんやり見守るだけでした。

反省

自分で埋め込んだバグにずいぶん苦しんでだいぶ時間を浪費してしまったので、もっとアプリケーションの動作とクエリの結果を意識して気をつけて変更していかないとな、と思いました。
出来ることももう少し増やしていかないとなぁ。
本選、頑張ろう。