たごもりすメモ

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

多言語対応User-Agentパーサライブラリ Woothee 1.0.0リリース: OS versionの出力をサポート

地道にバージョンアップを繰り返しているUser-Agentパーサライブラリの woothee ですが、このたび 1.0.0 をリリースしましたのでお知らせいたします。

http://woothee.github.io/

今回は前からやろうやろうと思ってたOSバージョンのparseをサポートしました。特にスマートフォン(iOS/Android)のバージョンが気になる人が多いようで、手元でもあったらいいなというケースが多くなってきたため、えいやと入れました。

ただし今回はあまり時間がなかったので、とりあえず自分で使いそうな以下の言語のみリリースしています。

以下の言語についてはまだ対応していません。誰かPull Requestを送るといいと思います。

現状こんな感じですね。

便利だと思うので、どうぞみなさんお使いください。

10/28追記

PHPも 1.0.0 対応されました。 @okonomi さんによるものです。
https://github.com/woothee/woothee-php/pull/13

Rubyでdynamic scopeを(メソッド定義だけ)実現する dyna_mo を書いた

ついカッとなって書いた。たのしかった。

https://github.com/tagomoris/dyna_mo

どういうことかというと、つまりこういうことだ!

require 'dyna_mo'
 
module MyModule
  class MyClass
    attr_accessor :num
    def initialize; @num = 0; end
    def name; "name"; end
    def sum(numbers); @num + numbers.reduce(:+); end
  end
end

こういう割と普通なクラスに対して

dynamo_define('MyModule::MyClass', :mytest_case_default) do
  def_method(:initialize) do
    @num = 1
  end
  
  def_method(:name) do  # #name を差し替える
    "dummyname"
  end
  
  def_instance_method(:name, :mytest_case1) do # #name 差し替えの別パターン
    "dummyname1"
  end
  
  def_method(:sum) do |numbers|
    @num + numbers.reduce(:+) + 1
  end
  
  def_class_method(:create) do |init_num=0| # 元の定義になかった MyClass.create の追加
    obj = self.new
    obj.num = init_num
    obj
  end
end

特定のとき*1にこのメソッドをこういうふうに上書きしてね!*2 ということを指定して

class SynopsisTest < Test::Unit::TestCase
  def test_synopsis
    assert { MyModule::MyClass.new.name == "name" } # 元の状態

    obj = MyModule::MyClass.new
    
    assert { obj.num == 0 }  # インスタンス変数 @num は 0
    assert { obj.name == "name" }
    
    dynamo_context(:mytest_case_default) do # ここから定義をスイッチ
      assert { obj.num == 0 } # #initialize is not overridden # これは @num の参照だけで変更なし
      
      assert { MyModule::MyClass.new.name == "dummyname" } # ウヒョー!
      assert { obj.name == "dummyname" } # 生成済みのオブジェクトのメソッドだってもちろん変わる!
      
      assert { MyModule::MyClass.new.num == 1 } # initializeだって上書きできる!
      
      assert { MyModule::MyClass.new.sum([1,2,3]) == (1+(1+2+3)+1) } # 普通に引数だって与えられる!
      
      assert { MyModule::MyClass.create(100).num == 100 } # ちょっとこのテスト専用に便利メソッド!
    end
  end

こういう感じで使う! dynamo_context(:mytest_case_default) に与えているブロックの中でだけ MyClass#name や MyClass#sum の動きが違ったり MyClass.create が使えたりする!!!!!!!!!!!!!!!!!!!
コンテキスト外で作った obj のインスタンス変数 @num は変化していないがそれを使うメソッドだけが書き変わっている! ウヒー!

もちろん別のコンテキスト名を指定すれば別の動きかたをする!

class SynopsisTest < Test::Unit::TestCase
  def test_synopsis
    dynamo_context(:mytest_case1) do # もちろん違うコンテキストでは違う動きになる!
      assert { obj.name == "dummyname1" }
    end
    
    dynamo_define(MyModule::MyClass, :onetime_context) do
      def_method(:name) do
        "onetime"
      end
    end
    
    dynamo_context(:onetime_context) do # 違う! 動きに!!!
      assert { obj.name == "onetime" }
    end
  end
end

もちろんだがダイナミックスコープなので、直接記述されていなくても、与えられたブロックからの呼び出し階層のどこかに存在するメソッドであれば容赦なく書き変わっている。例えば次のコードは MyClass を継承して MyClass2#name を呼んでいるが、親クラスの宣言は :onetime_context 内では書き変わっているので自動的に MyClass2#name の結果も変わる! カッコイイ!!!!!!

module MyModule; class MyClass2 < MyClass; end; end
 
class Synopsis2Test < Test::Unit::TestCase
  def test_onece_more
    dynamo_define(MyModule::MyClass, :onetime_context) do
      def_method(:name) do
        "onetime"
      end
    end
    
    dynamo_context(:onetime_context) do
      assert { MyModule::MyClass2.new.name == "onetime" } # 継承元の書き換えがここにも影響!
    end
  end
end

このような感じで自由の地平が開けます。ぜひどうぞ。

FAQ

なんで作ったの?

RubyKaigiの質問コーナーでdynamic scope欲しい!って言ったらMatzに「dynamic scopeを使わないためにRubyを作っている」と答えられたのでムシャクシャしてやった。

なんで作ったの?

power_assert とかちょっと真面目に使ってみたかった。が、まだちょっとバグいね*3

なんで作ったの?

RubyHirobaでうまい方法ないかなーと思っていたら @_ko1 さんにスレッドローカル変数とModule#prependを使うという方法を授かったのでついやってしまった。責任の半分は @_ko1 さんにあると思う。

本番で使っていい?

絶対にやめましょう。ありとあらゆる不幸が振りかかります。

本番で使っちゃおうかな?

万が一にでも自分が数年後とかに転職した先で見付けた本番コードで使われてたりしたら、たとえ退職者であろうとも書いた人のリアル住所を割って襲撃に行くかもしれません。やめておいた方が賢明です。

ぶっちゃけこれ必要なのって設計が悪いんじゃない?

はい。

例えばマルチスレッドをゴリゴリ使いまくっているのに超大事なところにシングルトンオブジェクトが居座っているFluentdというソフトウェアなんかがあって、そいつがマジでもうどうにもならなかったりするので作りました。しかしFluentd v0.12 ではそのシングルトンオブジェクトが排除されたため不要になりました。おおお……。。。

まあ、しかし、外部gemの動作に依存したコードのテストを書かなければならない時ってあるじゃないですか。ナントカAPIとかナントカSDKみたいなgemを叩きたいんだけどそいつがバックエンドをモックに置き換えられなくなってるとか! あるでしょ!

たのしかった?

はい!!!!!!!!!!!!!! 特異クラスに対してprependとかしてて脳から変な汁が出そうになった!!!

*1:この場合は :mytest_case_default や :mytest_case1 を指定したときだけ

*2:あるいは追加してね!

*3:変なことばっかりやってるテストコードなので再現条件が難しい、が、何がどうfailしたのかがたまに出なかったりした。そのうちちゃんと調べてフィードバックしたい。

「ZooKeeperによる分散システム管理」を読もう、という話

Zookeeperは現代の分散システムに不可欠なミドルウェアで、メタデータの管理、更新通知、リーダー選出といった問題を解決する。自分でZookeeper APIを叩く人は少ないかもしれないが、今やHadoop Namenode HA*1もYARN ResourceManager HAもHBaseもZookeeperを要求する。とにかく使う必要がある、という人は今や多いのではないだろうか。

こういったソフトウェアが何故必要なのか、どういった役割を持つのかについて、明快な回答を返せる人はあまり多くないのではないだろうか。Zookeeperは必要性を喧伝され、あちこちで使われ、しかしいまだに、何台で動かす必要があるのか、どのように運用されなければならないのか、ということをまとめて学べる資料などは多くない。

ということで、この本を読もう。

「2.2.1 Zookeeperのクォラム」。個人的には、自分にとってはこの一節のみでもこの本には価値が十分にあると断言できる。なぜZookeeperは3台以上の奇数ノードで構成するのが良いのか、3ノード構成においてクォラムを2とするのはなぜか。3ノード構成で1ノードに障害が起きても問題が起きない、としてよいのはなぜか。

読もう!

*1:ZKFC、Zookeeper Failover Controller

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ゲフンゲフン

RubyKaigi 2014いってきた&LTやってきた

RubyKaigi 2014参加してきた。ここのところ続きに続いたカンファレンス・イベント参加もとりあえずこれでひと段落。

RubyKaigi 2014, 18-20 september

正直にいって疲れが限界に近かったので、あんまり気合いを入れてtalkを聞いたり人と話したり……ということにはならず、おおむねぐったりしてた。ソファあたりとかホールの後ろのほうの席とかで。それでもこれまでなんとなく意識してたけど話したことはなかった人、みたいな人とは数人話したし、まあよかったんじゃないかな。と思う。たぶん。

1日目のパーティーの後に飲みにいったらなんとなくLaurentといっしょになったのでうだうだ英語で話してたのは個人的には超面白かった。世界のあちこちに行ってる人の話はいいなあ。というか、そういう人が大量にやってきてくれるRubyKaigiはすごいな。

Talkの内容はもう @tmm1 のクロージングキーノートが圧巻で、パフォ厨かくあるべしというか、スゲーな、という。あの話、改善したって話もすごいんだけど、*1各種の数値をちゃんととること、それをきちんと可視化してることも本当にすごいと思う。

あと今年は全体的に「Rails以外」にすごい力が入ってるなーというか、HTMとSTMの話が続いたあたりとか、スケジュール構成がすばらしすぎたと思う。なんなんですかねいいぞもっとやれ。*2
なおRails関連セッションはまったく見てないのでよくわからない……。

LTやってきた

YAPC期間中の疲れてぐったりしてる時に「さあLTを応募するんだ!」的なtweetを見掛けて、その衝動のままについうっかり応募したら通ってしまった。ので、やってきた。

話は単純で、v0.x.y なバージョンってなんとなく逃げな気がするからちゃんと v1 以上のバージョン番号つけようぜ! という話でした。Norikra v1を出したあとに考えたことがだいたい詰まってます。

Fluentd はまだAPIを壊す(というかデフォルト動作を変える)余地があるので v0 だし、自分の Fluentd plugin も基本的には Fluentd v1 が出てからあわせて上げようと思っている、ので、そこについてはいいんだけど……実際には自分の他のプロダクトもまだ v0 のままのものが大半で、よくないなーとは思っている。
いくつかは上げるための準備をやろうってことで、RubyKaigi中からぽちぽち始めてた。そのうち出します。

LTそのものは、コードをごりごり書いてる方々からポジティブな評判をいただけたので、ちょっと嬉しかった。しかしUstreamで見直すと英語がもう本当にボロボロだし、かなしい。もうちょっと発音をマトモにする練習しないとかなあ。

さて

次は2ヶ月先の Rubyconf2014 にproposalが 通った ので、初のアメリカ行きになりそう。それまではちょっと発表とかは置いておいてちゃんとコード書く期間にするつもり。

ああ、でもISUCONあるなー。がんばる。

*1:当人にとってはたぶん当たり前すぎて省略した

*2:RubyKaigi 2015はいまのところちょっと待ってねという状況らしいけど。

gcp ja night 28いってきた&しゃべってきた

gcp ja night #28 - connpass

BigQueryの話などがあるということで、自分もお誘いを受けたので適当にしゃべってきた。BigQueryそのものは前のほうで naoya_ito という人がやるらしかったので、Fluentdの話でもしようかなと。

結果的に streaming inserts api の制約についての話などがそこそこ新鮮な人もいたようだった。

聞いた話とかもいろいろ面白かったけど、さすがに人が多過ぎてどういう人が来ているのか把握するのも困難、というのと、BigQueryまわりとDockerまわりの人が混ざってたのかなーという印象。
個人的にはもうちょっと話題を絞った勉強会のほうが好みだな。

なんにしろ @kazunori_279 さん、いつもいつもお疲れさまでした。楽しかったです!

HadoopCon 2014 Taiwanいってきた&しゃべってきた

先日お知らせした通り、HadoopCon 2014 Taiwanで発表の機会をもらったので、行って発表してきた。旅費はゲストとして主催者から出してもらうような話もいただいたけど、手続等いろいろ考えて、今回も勤務先であるLINE株式会社に出してもらいました。

基調講演ではLambda Architectureの話をしてほしいとのことだったので、最近Hadoop Conf Japanでちょっと触れて考えがまとまりつつあったあたりをざーっとやってNorikraに繋げるような感じで話した。

Tech talkのほうでも発表した。そっちは完全にNorikraの紹介。海外で発表したのはこれが初めてのはず。ユーザが増えるといいなあ。
「あれ何人で作ったの?」「ひとり。自分だけ」「ひとり!? マジで???」みたいな会話をしたので、だいぶグレートなソフトウェアを作ったんだよ! という感じが多少なりと伝わってたんじゃないかなあ、とは思う……。

どちらのセッションも大事な点についての質問がちゃんと出たので、話は伝わってた、と期待してる。特にキーノートの方はだいぶ普段(特に英語で)話し慣れない話題について触れていたので練習も含めてかなり苦労したけど、なんとか終わったので良かった良かった、というところ。40分の時間制限のセッションで、前夜の練習ではちょうどくらいだったのに、終わってみたら10分余ってたときはマジかよと思った。英語での発表の経験をもっと積まないとなあ。

カンファレンスの感想

話した疲れもあってあまりtalkは聞けなかったけど、それでもというかその代わりにというか、色々な人と話した。台北の開発者コミュニティの話、向こうでのカンファレンスの空気、HareDBのマスコットのうさぎが可愛い話、日本のアニメやドラマがその週のうちにTVで放映されるらしい話、あれやこれや。

技術の話もそうでない話もいろいろだったけど、結局どこでもその場所で働いてる人がいて、その人達にとって必要なソフトウェアを作っていて、お互いに何をやっているかを知れれば自分が作らなくてもよかったものもあったかもなあ、などなどなど、あれこれ考えることもあった。

そういうことを考えながらあれこれ話してたんだけど、もちろん流暢な英語を話せるわけじゃなくて、英語と日本語と中国語を駆使しながら、なんとかかんとか話す*1ことになる。
でも自分が何かを言おうとしていれば相手はちゃんと聞いてくれるし、相手が何か言っていることが理解できなければ聞き返す。そういう当たり前のことをやっていると、ああ、普通にコミュニケーションってこういうものなのかな、とか思えてきていた。

観光

アジア1高いという台北101を見て上の方にあるレストラン(86階!)で中華料理食べたりとか、蒋介石を記念した公園と記念ホールを見たりとか、台北の伝統的朝食なのを食べたり、龍山寺を見たり、夜市に行ったり。特に2日目3日目は地元の人に連れて回ってもらい、あれこれ行った。自分達だけだと絶対行かない感じだったぞあれ。

ちょうどいい天気で、外にいれば風があって暑過ぎず、あちこち見た。海外旅行経験が非常に乏しい自分だけど、こういうの見てると、国内だけじゃなくて海外に行くのもいいなあと思えた。

そして、うまいものを食べた。

食べた。

それはもう食べた。

正直に言って食事の回数よりも胃袋の事情の方が厳しかったので、もっと食事の回数を増やすために長期滞在しなければならないと思う。2泊で帰っちゃったんだけど、もう1泊くらいしてくればよかった。無念。

まとめると

本当にすばらしかった。台湾で歓迎してくれた人々には感謝の言葉しかない。
勤務先のLINE株式会社にも、当然旅費の負担から、発表の準備についてまで、多大な協力をいただきました。ありがたい職場だと思う。

特別なきっかけがあって行くことになったわけでもない。CFPが出ていたので、英語での発表もいいかなと思って申し込んでみただけだ。誰にでもできる。台湾は特に渡航費も安いし、3時間のフライトで着く。みんながもっと気軽に行けるようになるとすばらしいんじゃないかな。そうなるといいと思う。

さて

次はー、今夜 gcp ja night 28 で少しだけしゃべります。あと今週後半の RubyKaigi でLTをします。
で、その先、たぶん一度か二度、アメリカで話す機会がありそうです。そっちも楽しみ。

*1:日本語がものすごく上手な人が2人いて、英語で話しててうまく通じないケースで日本語と中国語の仲介をしてもらったりとかしてたw