チーム「さばかんちゃーはんかれー」でISUCON11に参戦してきました。

毎年恒例のISUCONの参加ブログです。今年も例年と同じメンバーで戦ってきました。

まず結果からですが残念ながら予選敗退してしまいました。 今年はスコア的には本戦出場ラインだったんですが、追試で失格になってしまいました。悔しい。

それではやったことを振り返ってみたいと思います。

準備

特別何か対策はしていませんが、蓄積された秘伝のスクリプト郡を今回用のリポジトリを立ち上げてぶち込んでおきました。 ビルド、デプロイ、ログ収集を全てMakefileから実行できるようにしており、使い慣れたメンバーだったので非常にスムーズに快適に使えました。

開始前まで

10時開始予定だったので9時に集合して、軽く作戦会議。 各自の担当と開始時の動きを確認。

10:00 - 12:00

私のAWSアカウントを使用する予定だったので、CloudFormationのテンプレートを落としてきて環境構築。 その間、他の二人にはレギュレーションの読み込みと、dstat,kataribe,pt-query-digestで各種メトリクスを取得できるよう仕込んでもらう。 明らかに遅いクエリにindexを貼りスコア21,868。

12:00 - 13:00

  • indexを調整
  • jiaServiceURLをconst化
  • クエリで不要なカラムを取得しないよう変更
  • trendで不要なレコードを取得し過ぎているので LIMIT 1 を追加
  • AppとDBのサーバを分離

この時点で35,225

13:00 - 14:00

  • デフォルトアイコンのキャッシュ
  • postIsuCondition でバルクインサート対応
  • dropProbabilityを0.8に下げる
  • 画像をDBからローカルファイルに移す

この時点で42035

14:00 - 17:00

  • index調整
  • GET時の不要なトランザクションを削除
  • isu_conditionテーブルのみを別DBにする
  • trendをキャッシュ→失敗してリバート

ここではあまり効果が出ず38,066

17:00 - 18:00

このままでは勝てないので何か大きい手を打つ必要がある。
改めてボトルネックを確認すると、とにかくisu_conditionテーブルの負荷をなんとかする必要がある。
今までメトリクスの結果からgetTrendの処理に目がいっていたが、よくよく処理を確認するとgetIsuConditionsFromDBでレコードを対象に取得し過ぎていることに気づく。
安易にLIMITを入れるとfailし、一旦不要なループを抜ける処理を追加40043

18:00 - 18:30

ポータルのメンテによるロスタイム。
getIsuConditionsFromDBの改善を再び検討し、conditionLevelが一部でフィルタされていなければLIMIT指定できることに気づく。
LIMITを追加し、66,681

18:30 - 18:45

isu_conditionテーブルの負荷が下がったのでdropProbabilityを下げられるはずということでチャレンジ。
0.1ずつ下げていき、最終的に完全に取っ払う。
147,684でFinish。

終結

運営による追試の結果 138,574。本戦出場ラインは突破! しかし、画像をローカルに保存する対応で再起動時に画像が吹っ飛んでしまい、失格に。。。

感想

失格は悔しいですが、今年も非常に楽しかったです。 チームワークも洗練されてきて、メンバーの得意分野で無駄なく動けている感じでした。 メンバーがメトリクスをgit管理してくれてたのが非常に見やすくてGJでした。

@matsukaz@kz_morita 今年もありがとうございました!懲りずにまた一緒に戦いましょう!

ではまた来年!

チーム「さばかんちゃーはんかれー」でISUCON10に参戦してきました。

今年もこの時期恒例のISUCONに参戦してきました。ISUCONの参加記事しかこのブログで書いていない気がします。

さて今回でなんと5度目の参加です。結果は残念ながら予選敗退で、念願の本線出場とはなりませんでした。 途中までは割と良いペースだったんですが後半伸び悩んでしまいました。

また今回はメンバーで一ヶ所に集まらず、Googleハングアウトで常時繋いでコミュニケーションをとっていました。 意外とスムーズにやりとりできたのですが、細かい確認がしにくかったり微妙にタイムラグが発生したりしていたので、 わずかな時間も貴重なISUCONでは、できるだけ集まってやった方が良いなと思いました。

それではやったことを振り返ってみたいと思います。

開始前まで

当初は10時開始予定でしたが12時予定に延期されたため、会社から参加する予定だった私は家を出る時間を調整し、子供の世話などをしていました。

また、開始まで少し時間がありそうだったので、メンバーと協力してSlackに各種メトリクスを通知できる仕組みを整えていました。

pt-query-digestkataribepprof あたり分析結果を飛ばせるようにし、最後まで重宝していました。

開始(12:20) - 14:00

開始が12:20に延期になり、開始直後もポータルの不具合でサーバリストが見えなかったり、ベンチが走らせられないような状態でした。

とはいえまずはレギュレーションの読み込みから開始。どこかで聞いたことがあるような名前の不動産のサービスが舞台のようでした。 今まではあまりみたことのない bot からのアクセスの記述があり、未実装機能とのことでした。

サーバにも入れないのでこいつを nginx ではじくことを想定してnginx.confの調整を開始。 しばらくするとポータルは依然として見えないものの、自チームのIPが特定でき、sshできることを確認。 3台目だけなぜかsshできませんでしたが、運営が諸々対応中とのことだったので一旦放置。

ソースコードや各種ミドルウェアやOSの設定をgitにあげ、ビルドやデプロイ環境を整えていました。 とりあえずブラウザからアクセスしてみて機能を確認。 なぞって検索ってこれはヤバそう…となりました。(最後まで苦しめられました)

13:30頃にポータルも復活し、初回ベンチを実行。スコアは472でした。

14:00 - 16:00

とりあえずpprofをみてみたところjsonエンコードが重そうということでエンコーダを入れ替え。

またpt-query-digest結果の上位のクエリに対してとりあえずインデックスを貼りました。(estateのrent, chariのprice/stockあたり)

ちなみにインデックス効果のチェックのため、ローカルのDBにinit.shでデータを突っ込み、explainしながら確認していました。

このときのスコアが629。

16:00 - 18:00

search、特にnazotteが異様に遅い。とりあえずestateの緯度経度のインデックスを貼ったりしていました。

また前回の反省を踏まえ、AppとDBのサーバの分離を開始。1台目はそのままリクエスト受け、2台目をDBサーバとしました。 この時、別IPの接続をDBが受けれなかったため、bind-adressを外したり、DBユーザにIP許可を追加したりしました。

この時のスコアが713。

18:00 - 20:00

引き続きnazotteと格闘。クエリを削ってアプリケーションを改善し、スコアが1000台にのる。

ここで負荷はDBが支配的だったためレプリケーションを組むか検討。しかし残り時間で組み切るにはリスクが大きいため断念。 またsshできなかった3台目がまだつながらなかったため運営に問合せ。すぐに修正して頂きました。 (終了後、他チームの方がそもそもテーブル別にDBを分割してたと聞いてなるほどなーって感じでした)

20:00 - 21:00

最後までnazotteと格闘。DBのバッファサイズを調整したり、ログを切ったり、アプリケーションを細かく調整したりしてベストスコア1397まで行きましたが、 逆転の一手を打つため試行錯誤するも届かずフィニッシュ。

感想

今年もめちゃくちゃ疲れましたが、非常に楽しかったです。 例年よりもコード量が抑えられている割に思ったようにスコアをあげるのが難しい素晴らしい問題でした。

また今年は参加チームも非常に多く、運営の皆さんがすごく奮闘しておられました。大変にありがとうございました。

そして @matsukaz@kz_morita は三度も一緒に戦ってくれ感謝の限りです。本戦に連れていけないのはひとえに自分の力不足を感じて申し訳なく思います。

また腕を磨いて挑戦したいと思います!

チーム「さばかんちゃーはんかれー」でISUCON9に参戦してきました。

昨年と同じメンバーの @matsukaz, @kz_morita と共に今年もISUCONに参戦してきました。
最終スコアは 8860 で 予選通過ラインの 10000 には届きませんでした。あと一手何か入っていれば行けたかも。悔しい。
とはいえ今回はあまり対策という対策はしていなかったので思ったよりは戦えていたようにも思います。

やったこと

起床 - 10:00

今年も集合時間を指定しておきながら、自分が一番到着が遅かったです。みんな優秀。
簡単に作戦の打ち合わせをしたり、環境の整備などをしていました。

10:00 - 12:00

インスタンスを立ち上げて、ソースコードや各種設定ファイルをGit化したのちにレギュレーションを読み込み。
また昨年はいちいちsshしてインスタンス内でbuildかけたり、git pullして反映させたりするのがかなり面倒だったので、 ローカルでビルドしたり変更した設定を、コマンド一つでデプロイできるようにスクリプトを整備。
初期スコア2000くらい。

12:00 - 14:00

nginxで静的ファイルを返すよう対応。
また、やたらとロックをかけていたので明らかにいらなそうなロックを解除したり、冗長なクエリを除去したりする。
あまりスコアにインパクト無し。

14:00 - 15:00

今回は外部サービスが各所で呼ばれているが、明らかに不要な呼び出しを削除したり、シーケンシャルに呼び出している箇所を非同期化。
他のメンバのクエリ改善やインデックスの最適かもあり、ここで一気にスコアが6000くらいまで伸びた。

15:00 - 16:00

キャンペーンを調整してみるが、max connectionに当たってfailする。go側でコネクション数制限すれば、通るには通るがスコアが上がらないため最終的に戻す。

16:00 - 17:00

レギューレション内に新着情報をユーザにあったものにすれば購入されやすくなるとの旨があったので、getNewで返す内容を検討。
販売済みの商品を返さないようにするとややスコアが上昇する。
またユーザの購入履歴をみると同じカテゴリの商品ばかり購入しているので、ユーザの購買履歴にあるカテゴリの商品だけ返してみる。
これは件数が違うとかでエラーになるため諦める。
この辺りで、他のメンバのキャッシュ対応やN+1改善が入り、8000くらいまでスコアが伸びる。

17:00 - 18:00

外部サービス呼び出しをもう少し非同期化できそうだったで修正を開始するも、複数台構成にした方がインパクトがありそうなので中断。
しかし、複数台構成がギリギリ間に合いそうになかったので安全策をとって、軽微な修正に留める。
最後はベンチガチャで良さげなところでfinish。

感想

今年の問題は色んなポイントが盛り込まれていて、すごく面白い問題でした。すごく疲れましたがめちゃくちゃ楽しかったです。
ほぼぶっつけ本番だった割には、あまり手が止まるようなこともなく作業ができていたように思います。ただその分、地力の足りなさが感じられる結果でした。
来年もリベンジで挑戦したいです!

チーム「さばかんちゃーはんかれー」でISUCON8に参戦してきました。

チーム「さばかんちゃーはんかれー」でISUCON8に参戦してきました。
最後fail出てしまったんですが、終了間際のスコアが29000ほどでした。いずれにせよ本戦には届きませんでしたね。
個人的には2年ぶりの3度目の挑戦だったので、今年こそはと思い、数ヶ月前から同僚に声かけをして、 過去問練習したり、スクリプトの準備をしたり結構頑張ってきました。それだけに悔しいです。
しかしながら一人ではここまで戦えませんでした。一緒に戦ってくれた @matsukaz, @kz_morita には感謝の限りです。
1人チームの人すごい。

チームでやったこと

起床 - 10:00

昼飯を買い込んで、オフィスに集合しスタンバイ。
前日にメンバーに余裕を持って来いと偉そうに言ってた自分が開始ギリギリでした。
セッティングしといてくれてありがとう。

10:00 - 11:00

  • レギュレーションの読み込みと、ログイン環境の整備。あとappやconfをリポジトリにぶち込んで行く。
  • netdata, kataribe, pt-query-digestをインストール。
    • その過程でh2oの存在に気づきチームがざわつく。
    • h2oのパフォーマンスも十分かつ、nginxに置き換えたところでそこまで仕事をさせる場面はなさそうと判断し、h2oのままで行くことに決める。
  • 参考実装をgoに切り替えて初期ベンチ。1000くらい

11:00 - 12:00

  • 事前に仕込んでいたスクリプトを今回ように修正
  • netdata用にポートを解放
    • netdataでCPUを使い切ってることを確認。特にmysqlがCPUを食っている
  • my.cnfをシンボリックリンクにしたところmariaDBが読み込んでくれず若干ハマる。結局元に戻す。
  • カーネル系の設定をとりあえずぶち込む
  • for update を一部クエリから雑に剥がしてみてベンチ。1100くらい

12:00 - 13:00

  • pt-query-digestが動き始める
    • 情報を元にindexを追加
    • indexが効くようクエリを修正

13:00 - 14:00

  • kataribe用にh2oのログを調整
    • ルートのアクセスが多く重いことを確認
      • getEvents -> getEventの多段N+1がやばいので修正を開始
  • h2oの静的ファイルの304対応

14:00 - 15:00

  • getEventのN+1を修正しベンチ。20000くらいに跳ね上がる

15:00 - 16:00

  • この辺りでサーバを複数使用することを検討
    • app2台とDBで分離する方針に決定
    • DBの分離だけ先に対応
      • 鈴木明さん問題に遭遇。初期化shellの接続先をDBサーバのhostに修正。
      • CPUのボトルネックがappのみに
  • この時点で26000くらい

16:00 - 17:00

  • DNSラウンドロビンでappを2台に振り分ける
    • appのCPUに余裕ができる
  • 再起動対策でDBの暖気設定を入れる
  • h2oのmaxconnectionを引き上げ
  • sheetsテーブルを使用しないよう修正
  • ベンチがfailしまくりだす

17:00 - 18:00

  • getEventで既存コードを異なる挙動になる可能性のある箇所を修正
  • 怪しいコミットをrevertし再ベンチ。29000くらい
  • しかし最後またfailしてしまいfinish

良かった点

  • 割と早めにgetEventに対応でき、一時上位争いに食いこめた
  • netdataがすぐに入れれてボトルネックもある程度絞れるので超良かった
  • profileツールのインストール,on/off,プロファイル実行周りをスクリプト化していたので、プロファイルを取るのが捗った
  • アプリのビルド、再起動周りもスクリプト化していたので捗った
  • h2oをそのまま使って良かった。nginx入れてもやはりあまり効果はなかったと思う。

反省点

  • my.cnfなどの環境構築周りでミスってかなり時間を使った
  • 不必要な暖気設定のために、MariaDBのバージョンをあげるためにMariaDBを再インストールする労力を割いてしまった
    • レギュレーションをちゃんと読めていなかった
  • 3人の役割分担が曖昧で冗長に作業をしたり、手が止まる時間があった。

感想

結構戦えた感触はありました。
夕方頃に暫定1位になったりして興奮したりもしましたが、最後は力不足でスコアを上げきれませんでした。
また、提出スコアが再起動後に最終試験に行うものと勝手に思い込んでしまっており、DBの暖気設定を入れるなど無駄な時間を使ってしまいました。
マジでレギュレーションはちゃんと読まないとダメですね。。。
来年またリベンジしたいと思います!

ISUCONに初参戦して砕け散ってきました

初ブログということで、テーマは先日初参戦したISUCONについて。

 

isucon.net

 

ISUCONWebサービスを限界まで高速化を図るチューニングバトルです。

クライアントからサーバサイドに転向して約1年が経とうとしているので、力試しも兼ねて参加しました。

 

チームは自分含め3人。メンバーのdo7beが書いたブログは以下になります。

ISUCON5予選で最高スコア1万弱を出しました - do7be.exports

 

役割

 役割はそれぞれ分けて、

として、自分はミドルウェアを担当しました。

特段ミドルウェアの領域に強いという訳ではないのですが、他のメンバーの実力を最大限発揮できることと、自分自身もっと強化したいと思っていた領域なので丁度よいかなと。

 

やったこと 

時系列で以下のようなことをやりました。

開始前
  • 自社オフィスに集合。みんなかなり早く集まったようで、メンバーから生存確認の連絡を受ける。
  • 設定ファイルやソースの運用方法を確認。
  • 鍵やインスタンスの確認。
10〜12時頃
  • インスタンスを他のメンバーが準備している間にレギュレーションを読み込む。
  • 鍵がうまく登録できずトラブル。作り直して無事入れるようになる。
  • 言語の設定確認。PHPでいく方針だったので、systemdに戸惑いながらも初期のRubyからPHPに切り替えて、benchを流す。
  • 各種設定ファイルをリポジトリの管理下に置く。
11〜14時頃
  • kataribeをインストール。nginxのアクセログを収集し分析する。重いアクセスにあたりをつける。
  • pt-query-digestをインストール。DBのslow queryを全クエリ対象にしてログを収集。分析してDB、アプリケーション担当に共有してやばそうなクエリにあたりをつける。
  • nginxの設定をチューニング。
  • メンバーがコツコツクエリやアプリケーションを修正してじわじわスコアがあがりはじめる。2000点くらい。
14〜16時頃
  • ページレベルでnginxのキャッシュができないか試行錯誤する。結局今回のシステムでは決定的な改善ができそうになく断念。かなりの時間を使ってしまった。
  • その間にもメンバーがクエリ改善。6000点くらいまであがり、暫定10位前後で遷移。
16〜17時頃
  • 改めてDBのログを収集し、最後の調整に入る。
  • sysctl.confでTCP周りの設定を調整する。
  • メンバーがクエリを結構大胆に変更したりして、10000点弱までスコアが伸びる
17〜18時頃
  • 再起動テストをするとスコア激減してあせる。my.cnfが反映されていなかったことが発覚するが、反映しても再起動後スコアが落ちる。
    (終了後にmysqlのウォームアップが原因と知りとても悔しかった)
  • php7を入れたくなる衝動にかられるが、メンバーに止められやめる。

結果

最終スコアは7000点ぐらい(詳細は記録し忘れました)

最高スコアが10000点弱いってるだけに悔しかったです。またやたらと「タマキ」が表示されてbench結果が伸びない事象に遭遇し苦しめられました。ほかのチームも結構でててたみたいですね。

まとめ

ISUCONに初参戦し、見事に砕け散りました。

個人的にはWebに対する知見があまりないなか思ったより戦えたように感じ、少し自信がつきました。

もともとは3,4ヶ月前にくらいから同じ社内のメンバーの呼びかけで過去問を解いたりして勉強をしてきたのが予選で大きく役にたったと思います。

めちゃくちゃ疲れましたがとても楽しかったです。来年もあるなら参加して予選突破を目指したいと思います!