Erlangにhttpcなんてのがあるんだよな、Kyoto Tycoonたたくのが楽すぎる

R14Bの完熟走行ついでにtutorialにあるように、Kyoto Tycoonをたたいてみる。
以下、すごく大雑把なメモ。

1> inets:start().
ok
2> httpc:request("http://192.168.1.2:1978/rpc/set?key=korea&value=seoul").
{ok,{{"HTTP/1.1",200,"OK"},
     [{"date","Thu, 18 Nov 2010 07:30:20 GMT"},
      {"server","KyotoTycoon/0.9.8"},
      {"content-length","0"},
      {"content-type","text/tab-separated-values"}],
     []}}
3> httpc:request("http://192.168.1.2:1978/rpc/get?key=korea").           
{ok,{{"HTTP/1.1",200,"OK"},
     [{"date","Thu, 18 Nov 2010 07:37:18 GMT"},
      {"server","KyotoTycoon/0.9.8"},
      {"content-length","11"},
      {"content-type","text/tab-separated-values"}],
     "value\tseoul\n"}}

おー、簡単。
手元の本*1だとgen_tcpとかでlow levelの設定をした関数を定義して、、とかやっていたのが嘘のようだ。新しい本*2なら常識なのかな。
しかし、keep aliveとか細かい話をするなら、そこまで下りていかないとだめかしら、、いやいや、、httpc:request/4でも、下記のようにREST風にも叩けるし、httpc:request/5だとかなり細かい指定ができるようだ。Erlangで明示的にコード書くよりbuilt-in関数の方が速いかな?

4> httpc:request(delete, {"http://192.168.1.2:1978/korea",},,).
{ok,{{"HTTP/1.1",204,"No Content"},
     [{"date","Thu, 18 Nov 2010 08:06:08 GMT"},
      {"server","KyotoTycoon/0.9.8"},
      {"content-length","0"}],
     }}

そもそも何故MnesiaやCouch-DBを使わずにKyotoTycoon/Kyoto Cabinetなのか?

そりゃ、まずは好奇心。特に、いろんな特性のあるキャッシュDBをとっかえひっかえ試せて、Luaによる拡張も自由にできるところが楽しい。*3
そして、それぞれのDBの特性は、作者の豊富な経験が反映されている(と思う)。たとえば60秒でcacheからexpireするようにした場合、こんな応答が返ってくる。

19> httpc:request("http://192.168.1.2:1978/rpc/set?key=japan&value=tokyo&xt=60").
{ok,{{"HTTP/1.1",200,"OK"},
     [{"date","Thu, 18 Nov 2010 09:21:39 GMT"},
      {"server","KyotoTycoon/0.9.8"},
      {"content-length","0"},
      {"content-type","text/tab-separated-values"}],
     []}}
20> httpc:request("http://192.168.1.2:1978/rpc/get?key=japan").               
{ok,{{"HTTP/1.1",200,"OK"},
     [{"date","Thu, 18 Nov 2010 09:21:51 GMT"},
      {"server","KyotoTycoon/0.9.8"},
      {"content-length","26"},
      {"content-type","text/tab-separated-values"}],
     "value\ttokyo\nxt\t1290072159\n"}}

1分待つ

21> httpc:request("http://192.168.1.2:1978/rpc/get?key=japan").
{ok,{{"HTTP/1.1",450,"Logical Inconsistency"},
     [{"date","Thu, 18 Nov 2010 09:23:39 GMT"},
      {"server","KyotoTycoon/0.9.8"},
      {"content-length","34"},
      {"content-type","text/tab-separated-values"}],
     "ERROR\tDB: 7: no record: no record\n"}}

セッションデータはいちいち消すのが面倒だから、この手のDBに入れておいて、DBが勝手にやってくれるならありがたい。さらに、メモリやディスクが限られている場合には、expireするのを待たずに容量の上限で消す、という設定のDBまである。
こういう機能はrobustなサーバー構築には結構効く。試行錯誤でチューニングしている暇は現場には無いし、自分でwork aroundするよりも、ずっと信頼性高いソリューションになるだろう。

Erlang的にやりたいこと

あとは

  • shardごとにプロセスを立ち上げ(spawn)
    • KTのインスタンスの死活監視用のサブプロセスをspawnし、それをリンクする
    • 立ち上げたプロセスのPidを(consistent)ハッシュテーブルに登録。clientのID(IP address等)とrequest(URIとか)をconcatenateしたものについてハッシュ取る

するだけなんだけどなー。なんで手がつかないかなぁ。
ちなみに上記の監視プロセスのschemeについては、Erlangには"supervisor behaviour"というのがあって、one to one, one_for_all, rest_for_oneというのが選べるが、scale_outやfault torelantとかdegradableってのは無いので、自作する必要がある。そこがchallengeだな。*4

my_supervisor behaviourを自作するとすれば

参照:http://www.trapexit.org/Defining_Your_Own_Behaviourとかhttp://d.hatena.ne.jp/m-hiyama/20070913/1189669297
こんな感じ?監視しているといっても、自動的にプロセス再起動ではなくて、DBサーバーが落ちていたら、プールからはずす、ということもしたい。それを明示的にやるAPIとしてscan_availabiltyを付け加えるとこんな感じか。(そこを隠すべきかは悩ましい)

-module(my_supervisor).

-export([behaviour_info/1]).

behaviour_info(callbacks) ->
 [
 % init(WorkerSpec) -> {State, Spec}
  {init, 1},
 % attach_worker(State) -> NewState
  {attach_worker, 1},
 % detach_worker(State) -> NewState
  {detach_worker, 1}
 % scan_availability(State) -> NewState
  {scan_availability, 1}
 ];
behaviour_info(_Other) ->
    undefined.

まー、コード作る前に暇作んないとな。

追記:接続poolingか、監視か

my_supervisorは監視プロセスを統括すべきなのかしら。
接続poolingが効くのか、野放図にエンドユーザからのアクセス1つごとにErlangのプロセス1つ生成してしまうのがいいのかしら。
たとえば、同時接続数がめちゃくちゃ多くて、それぞれが低速なclientだったりすると、workerが枯渇してqueueが伸びることになる。
対応としては、clientの面倒をみるプロセスはどんどこ生成しておいて、そこからのアクセスをまとめてからconsistent hashingするということになるだろうか。
本当?
まー、やってみればいいんだな。Erlangなら「非常に多数の(数万)低速なclientからの同時接続」という状況を作るのは簡単だ。

*1:[asin:4274067149:detail]

*2:[asin:4873114659:detail]

*3:詳しくは作者のサイトの[http://fallabs.com/kyotocabinet/spex.html#tutorial:title=Fundamental Specifications of Kyoto Cabinet Version 1]のFeatures of Various Database Classesの項目をどうぞ

*4:Ericsson内部では使ってると思うのだが