2013年11月11日

ISUCON3 で惨敗してきました

はてなブックマークに登録

KLab からは 「ぜかまし」 が ISUCON 3 の本戦に参加していました。 メンバーは私 (@methane) と、 @tenntenn, @hasi_t です。

結果は順位なし (計測時に fail が多かった) と、惨敗というかそれ以前の問題です。

結果は散々だったものの、設問は非常に完成度が高く、とても楽しめました。 作問してくださった面白法人KAYAC様、主催してくださったLINE株式会社様、 環境と懇親会を提供してくださったデータホテル株式会社様、ありがとうございました。 特に問題作成に関わられた方、本当にお疲れ様でした。

それでは、どうしてこんな結果になったか、当日の様子を振り返ってみます。

10:00 会場着

3人とも開場時間前後には到着して、ホワイトボードがある会議室を抑えました。 (とはいえ、ホワイトボードは同じ会議室の人にも見えてしまうので、結局使いませんでした。)

@hasi_t がACアダプタを忘れるというトラブルも有り、基本的には @methane が単独で、 @tenntenn と @hasi_t がペアで2並行で作業しようということになりました。 @hasi_t は競技プログラマーでプログラミング能力は高いものの Go には不慣れで、 @tenntenn は Go には慣れてるけどまだ1人では不安なので、AC電源の件がなくても この分担は適切だとその時は考えていました.

10:45 問題発表

10:45 から問題が発表されました。 すでに公開されている通り、今回のお題は画像版 Twitter でした。 お題

この時点で、今回のアプリ内に long polling があるとわかり、 Go の実力が発揮できると 喜んでいました。 スコア算出方法で、画像投稿とタイムラインのレイテンシだけがレスポンスタイムによる ボーナスがあり、ここをチューニングしたら他のリクエストの10倍以上のスコアが獲得できます。

しかし、この時点で誤算がありました。 ISUCON 2 や ISUCON 3 予選では、クライアントが 複数種類に分かれており、得意な部分・スコアへの影響が大きい部分で全力でスコアを稼ぎ、 画像ファイルのダウンロードなどは sleep して帯域を開けるという戦略が取れました。 今回はまだクライアント側が公開されていませんが、多分 API 通信部分と画像ダウンロードが 別れておらず、 API をいくら速くしてもその分画像ダウンロードを速くしないと 画像配信が追いつかずタイムアウトで fail してしまうのです。

11:00 作業開始

IPアドレスなどを受け取り、作業環境を整えたり、スコア計算式から 1ms でレスポンスを 返した時のスコアを計算して取らぬ狸の皮算用をしたりしていました。

Go のプログラムを動かしたのですが、初期状態では fail してしまっていました。 (多分画像変換で外部コマンドを呼び出す部分が追いつかない)

初期構成では Apache でリバースプロキシをしていて、 ProxyPass に retry=0 を設定してなかったので 1度エラーが起こると立て続けにエラーになってしまいます。 Go を使う場合 リバースプロキシは不要なので、ろくにアクセスログも見ずに Apache を止めて Go を 80 番ポートで動くようにしてしまいました。

優勝チームはまずじっくり時間を書けてアクセスログを確認しているので、この時点で 負けが確定してしまったと思います。

12:00 実装開始, 昼食

まずテストが fail するのが多分画像変換ということで、画像変換を外部サーバーに切り出す 作業と、 timeline の更新を高速化する作業で別れることにしました。

画像変換は、 ImageMagick 以外のツールへの切り替えが危険なので、できるだけメジャーで ImageMagick バインディングが安定していて、なおかつ慣れてる言語で実装するべきと判断し、 僕が Python で担当することにし、@tenntenn と @hasi_t に timeline 高速化をお願いしました。

Python に画像変換を投げる Go の修正と、 Python のリファレンス実装からDBアクセスや認証を ごっそり落として画像変換に特化したサーバーを作ったのですが意外に手間取りました。 まず、 Go から別サーバーで動いてる Python に :5000 でつながらず、 telnet でつなごうとしても no route to ... というようなメッセージが表示されました。

インフラ屋がいない (僕がかろうじて Apache や nginx の設定をドキュメント読みつつできる程度) のチームだったので、 route という単語を見てネットワークの問題だと勘違いしパニックになったのですが、 :80 などはつながることに気づき、 iptables を off にしたらつながるようになりました。 (なんで connection refused ではなくて no route 的なメッセージが出たのかは今でもわかりません)

また、 Python の初期実装に問題があり、一時ファイルを経由して ImageMagick のコマンドを 呼び出すときに一時ファイルを flush してないのでコマンドが失敗していたり、 Python 側の サーバーにはまだ転送されてない画像が Go 側には前回のベンチマーク実行で存在していて、 データベースにも登録されていたために 404 エラーが起こったり、くだらない問題で時間を浪費して、 簡単なサーバーのはずがベンチマークを通った頃には15時を回っていました。

15:30 動かない、アプリ

画像変換を外部サーバー化できたので、 @tenntenn, @hasi_t チームの作業とマージしようと思い、 状況を確認したところ、予選の時のようにデータをメモリ上に持つようにアプリケーションを書き換えてる 途中でバグに苦しんでいました。

直感で、落ち着いてコード読めば直せると判断し、マージを @hasi_t にお願いして @tenntenn に コードを説明させつつ、バグを潰していきました。 バグを潰したあとは @hasi_t がマージした画像変換を取り込み、さらにペアならぬトリオプログラミングで 残りの部分を実装を完了しました。

16:30 通らない、ベンチ

これでアプリケーションはひとまず動くようになったのですが、ベンチマークが通らないことに 苦しみ続けていました。エラーを確認しても、全く見たことのない HTTP status が出てきます。

Go の http パッケージの中を探してそんなステータスを返しているところがないということを確認し、 このエラーはタイムアウトという意味だろうと判断し、最後に実装した部分に残っていたデッドロックを 解消しました。

その後エラーは icon や image に集中するようになり、そのエラーがタイムアウトとわかっていたのですが、 本当はやるつもりだった分散をする時間が残ってません。とりあえず付け焼き刃で変換結果をキャッシュするように したのですが、焼け石に水でした。

反省

最大の問題は、リーダーの僕が、仕様を聞いた段階でやりたいことを思いついてしまい、 ろくにベンチマークプログラムの挙動を把握しないまま方針を決めたことです。

役割分担も、事前に Go で Web アプリを書く練習ができていない @hasi_t に 画像周りを任せて、僕が @tenntenn で Web アプリ側を書くべきでした。 一番疑って調査することが必要な部分なのに、僕が指示をしてしまったことで、スコアに あまり関係ないことに気づくチャンスを失ってしまいました。

また、 Go は C 言語に比べれば圧倒的に楽なものの、マルチスレッドを扱える 言語ということで、ちゃんと使えるようになるにはある程度の慣れが必要で、 いくら頭がいい @hasi_t でも練習不足では荷が重かったようです (本人は node.js の コールバック地獄のほうがわかりやすいと言っていました)

そもそも、優勝者の Blog を拝見して、総合力に圧倒的な差を感じました。 ISUCON で使う言語と、毎日仕事で使っている言語が一致しないと、短い開発時間 (アプリの使用把握や提出前の最終確認などを除くと実質4時間強)で遭遇する落とし穴を すべて解決して優勝できる環境を作り上げるのは不可能です。

次回までにもっと実務での Python と Go の開発者を増やして、次回はしっかり アクセスログを確認して、作ってみたい設計ではなく勝つ方法を考えていきたいと思います。

songofacandy at 21:55│Comments(0)TrackBack(0)

トラックバックURL

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 
Blog内検索
Archives
このブログについて
DSASとは、KLab が構築し運用しているコンテンツサービス用のLinuxベースのインフラです。現在5ヶ所のデータセンタにて構築し、運用していますが、我々はDSASをより使いやすく、より安全に、そしてより省力で運用できることを目指して、日々改良に勤しんでいます。
このブログでは、そんな DSAS で使っている技術の紹介や、実験してみた結果の報告、トラブルに巻き込まれた時の経験談など、広く深く、色々な話題を織りまぜて紹介していきたいと思います。
最新コメント