読者です 読者をやめる 読者になる 読者になる

たごもりすメモ

コードとかその他の話とか。

LINE選抜で isucon4 予選に参加してPerlのコードをごりごり書いた

みなさんtagomorisはPerlなんて書いてねーだろと思うかもしれませんが、意外にちょっとは書くんですよという話……じゃなくて、それはどうでもよくて、今年も ISUCON の季節ですね、という話。

詳しくはこちらをどうぞ。 LINE選抜で isucon4 予選に参加してきました。暫定スコア「51192」を出すためにやったこと - blog.nomadscafe.jp

では済まないので、自分の視点からの話も。

準備

上述エントリのとおり、準備自体はほぼ去年のISUCON3本戦のものを持ち込む形。結果的にほとんど問題なかったと思う。

設計

10時になってからの開始後、サーバ側のセットアップと初期ベンチ実行および結果確認は@kazeburo、コードのバージョン管理まわり等は@sugyanにおまかせしてひと足はやめに詳細が公開されたレギュレーションとアプリケーションコードを読む側に回った。
その前にアプリの動作確認はもちろんやったんだけど、ログインしかできることがなくて面喰らって運営チャットで確認するなど。

今回も1時間以上はほぼ何も手を入れないで内容の確認。
さて、ということでどうするかの相談の第一のお題は、インメモリDBアプリにしたほうが最終的に高得点になるのが明らかだけどどうしましょうかね、という……。選抜チーム枠をもってることだし、本戦の練習のつもりで本戦で使えるアーキテクチャだけでやりますか、ということにした。まあそれでも予選通過できるくらいの点数は出せるだろう、と。*1

テーブルはふたつしかなくて users と login_log のみ。usersは更新が無いしそんな大した量でもないので全部メモリに読んじゃえばいいんじゃね、ということで、そのうち手が空いたらやることに*2

問題は login_log で、以下の制約をクリアする必要があった。

  • 割と面倒なSQLで状態を毎回計算している
  • しかも状態を計算するのに /login と /report で異なるSQLを用いていて、整合性がとれてるかどうかが難しい
  • 初期状態である程度のログが存在するので、データストアを変えるならこれを再現する必要がある
  • /report を出力するためにはデータストアを変える際の設計に注意が必要

さあどうしようかと相談をして、3人であれこれ検討した結果、以下のように。

  1. 一度 ban/lock された状態から復活することは無いようだ
  2. memcached で ip/user ごとにカウンタを持っておいて、そいつをincrすればいいんじゃないかな
    • そのカウントが閾値までいったら ban/lock すればいい
    • チェックは get だけで済む
  3. ban/lock されたらincrと同時にその ip/user をファイルに追記しとこう
    • そしたら /report はそれを全行読み出して json にして返すだけでOK!
  4. ただし初期状態での ip/user ごとのカウンタはmemcached上に用意してやる必要がある
    • 初期化用のスクリプトinit.plを用意してinit.shから起動してやることに
    • 初期データをMySQLにロードしたあと login_log を全行読み出し、1行ずつリプレイすることでオンメモリで状態を全部作る
    • 最後にログ中にあった ip/user の全状態を memcached に add すればOK

これでいけるんじゃないかな、と方針が決まったので、Webアプリ側の改修を@sugyan、初期状態の構築を自分が担当することにしてゴリゴリコードを書きはじめた。

経過

sugyan,tagomorisがコードを書いている間にMySQLにインデックス追加したりnginxの調整をやったりでスコアが一時的にトップに。完全にkazeburoパワーオンリーです。
コードの実装もそれなりにいいペースで進んでたのでそこで気分よくお昼ごはんにした。

Memcachedデータストア版の実装がほぼ終わり、MySQL版のコードにマージする形で両方のデータストアを更新しつつ、参照をちょっとずつMemcachedに寄せていったら、ちらほらとバグが。だいたいはすぐ解決したんだけど /report のチェックでこける問題が長いあいだ解決できなかった。commit log見るとこれに2時間近くかけたらしい。

で、最終的には動くものが揃ったんだけど、これで50000付近のスコアを出した頃には遥かに高いスコアを出しているチームが複数あり、うひー、という状況だったのでした。*3

インメモリDBアプリ

まあこんなもんですかね、というところで落ち着きかけたんだけど、あと30分くらいの時間を手持ち無沙汰に過ごすのもなんだったので、とりあえずやってみるかーとインメモリDBアプリ、シングルプロセス構成のコードに改造してみることにした。

login_log をインメモリにするだけだったのでそう難しいこともなくガッと改造したんだけど、ベンチを走らせてみると通常の構成*4と較べてほとんどスコアが変わらず。高いか低いかは微妙だけど、意味ないなーと思って諦めた。
まあやったのは普通に Kossy でのインメモリDB化なので、生PSGIアプリでゴリゴリ書いたらまた変わったのかも。そんな時間はなかったし、あんまりそこまでやる気もなかった。

ISUCON予選という競技

今回思ったのは、インメモリDB化を試すインセンティブが予選だとありすぎて、これはもう別の競技だなあ、ということ。
ベンチマークツールが同一サーバ上で動いているとどうしてもCPUパワーをベンチかける側と受ける側で食いあうので、最終的にはアプリをCPU1コアで動かし、残りのCPUコア全部をベンチツールに捧げるのが正しい、みたいになってしまう。*5

それはそれで正しいのかもしれないけど、複数サーバを効率的に組合せてハイスコアを叩き出す、という決勝の競技と較べると違いすぎてて、ちょっとなあ、という気持ちが抑えられない。
これはたぶん去年のISUCON3予選問題や今年夏のISUCON夏期講習環境が出回りすぎて、決勝の問題じゃなくて予選の問題の練習をするという方に慣れすぎちゃったんだろうな、という気がする。決勝の問題を環境から準備しようと思うとサーバが5台必要になってしまうので、必然的にそうならざるをえない、のかもしれない。うーん……。

まあ、そういうこと言ってる脇で、fujiwara組がこっちのチームと同程度の得点を叩き出した上でベンチツールの挙動を見越した点数アップを更にやっているので、負け犬の遠吠えかもしれぬ。はい。
あとたぶん、選抜チーム枠を持っていて安穏とした気分で予選に出てるからそういう余計なことを考えてしまうのかもしれなくて、他の参加者からすれば何をトボケたことをと言われることかもしれない。何しろ、いまや決勝出場人数の6倍もの人数が予選を勝ち上がれないんだもんなあ。その人達にとって、ISUCONとは予選のことなのだ。いやはや。

まとめ

何にせよ今回も楽しかったです!
運営の方々はいろいろ苦労があったかと/あるかと思いますが、決勝も非常に楽しみにしてます。予選通過の方々は決勝でお会いしましょう! 我々のチームは出られるのです! HAHAHAHAHAHAHA!*6

*1:結論から言うと、1日目終了時点での結果を見て、ウゲー、甘かったかなと思っていた。

*2:んで手が空いたときにさくっとやった

*3:後から話を見るに、はるか上というスコアのチームはどうもworkloadのバグだったっぽくて、インメモリDBアプリのチームはそんなには上じゃなかったっぽい?

*4:Starletのworker 8とか

*5:あるいはベンチマークツール自体の挙動を推測してそれに対応した修正をやるとか、ベンチマークツール自体のチューンをやるとか……他の参加者のblogエントリ参照のこと。

*6:みんなも主催会社の社員になれbゲフンゲフン