naoyaの日記 RSSフィード

2009-06-03

Web Hook について 17:11

Web Hook 機能ですが、今後、以下のようなことも考えてます

スターやIDコールとかも良いですね。

とりあえずは最低限の機能だけでリリースしました

キーが必須ではないのは、例えば他の人のブックマーク通知を受け取って何かするようなプログラムを公開して「Web Hook にこの URL を登録してね」というのができるように・・・ と考えています。複数そういうサイトがあったらどうするのか、というのはおいおい。

ほか

  • マルチバイトが落ちるのは、すみません、直します。
  • HMAC はそこまで考えれてませんでした、すみません。当面は今の仕様イキでいこうと思います。

miyagawamiyagawa2009/06/03 17:22HMAC は Google code の Post commit web hookで採用されてます http://code.google.com/p/support/wiki/PostCommitWebHooks が Google 固有の HTTP ヘッダ名になってるので、 Github とかでも同じようにして標準にしてくれないかなあ、と GitHub にリクエストをだしてました。
http://support.github.com/discussions/feature-requests/167-signing-webhook-as-hmac-using-a-shared-key

GitHub はユーザ名、パスワードで Basic 認証できるのでそれをつかいなよ、って方針みたいですが。
http://github.com/blog/237-basic-auth-post-receives

naoyanaoya2009/06/03 17:34ありがとうございます。

WebHook-Sign ヘッダ等で提供するなら現状のキーの仕様ともバッティングしませんし、後日よりセキュアな認証方法として仕様追加、でも大丈夫そうですね。検討してみます。

yappoyappo2009/06/03 17:56あざっす。とりあえず正座して待ってます。

おもちゃな段階が終わった頃に(開発者さま向け)とか書いてあるのは外した方がいいなーと思ったす。
それか Web Hok URLの登録は、hok受ける側のサービスではてな認証したら勝手に登録できるくらい簡単になるとか。
スターやらIDコールも、web hokのインフラ使うようになったら簡単な仕組みじゃないと色々大変そうすな。

naoyanaoya2009/06/03 21:36どうもです。

そうですね、実際には、開発者が作ったものをそうでない人が利用できるものなのに、開発者さま向けと書いていると自分とは関係ない機能だと思われてしまうということですよね。

はてな認証したら勝手に登録できる、というようなのは、public な web hook を作る人には良い手段ですね。

2009-03-30

http://blog.livedoor.jp/dankogai/archives/51193858.html 07:50

いろいろ指摘はありがたいのですが、アルゴリズムの話をするのに正直これは枝葉の議論じゃないかと思う。

for(;;) より foreach の方が速いのは知っています。この手の定数項のチューニングはそれこそ大量に入力があって定数項を減らしたいときにベンチマークを取りながら律速になってるところを少しずつ削っていけば良いと思います。

可読性に関しては、私の場合はアルゴリズムの話は C や C++ などで書かれたコード書籍を良く読んでいるから for(;;) の方が分かりやすい。配列にばらさない方がよいとかも同じ。(Pythonで xrange 使っているのはまだそれしかやり方を知らないんです。) 弾さんはそちらの方が分かりやすい、私はこちらのほうがわかりやすいというただの主観の話ではないかな。Perl は There is more than one way to do it でしょう?

こういうコーディングスタイルの押しつけみたいなの、○○すべき、みたいな話がウェブメーリングリストで多いから萎縮しちゃう人も多いですが、趣味プログラムなんて好きなように書いて好きなように晒せば良いんだよ。仕事でのコーディング規約じゃないんだから。

まあ、程度の問題なんで、use strict をまだ知らないとか、そういうのは教えてあげたら良いと思うし、Pythonソース書いてたら unicode() を教えてもらったとかそういうのはとてもありがたかった。Perl Best Practice の類は趣味の押しつけが行き過ぎてる。参考にはなるけど、そうしなければならないとは思わない。私もそんな風におもっていたこともありました、でもあれ良いと思ってた頃は、個人的になかったことにしたい。そんな年頃です。

トラックバック - http://naoya.g.hatena.ne.jp/naoya/20090330

2009-02-08

entrylist の仕様 08:16

の件ですが、一部誤解があるようです。現在仕様はどうなっているかと言いますと

となっています。

ですので、例えばみなさんのはてなダイアリーなどでは全ドキュメントに対して遡ることができます。また http://blog.livedoor.jp/dankogai/ のような場合も同様です。

http://finalvent.cocolog-nifty.com/ のような場合は、そのドメインエントリーのうち、直近 10,000 件しか遡れません。ただし、国内トップサイトのようなサイトでない限り 10,000 エントリー以上ブックマークされたエントリーがあるサイト (合計 10,000 件以上ブックマークを集めたサイト、ではありません) はまずありません。

という仕様ですので、個人が自分サイトを調べる分に、制限されることはまずありません。またベテランブログの記事を辿りたいという場合も同様です。

問題になるのはニコニコ動画全体、はてなダイアリー全体、といった特定のサービスを全体にわたって過去に遡る場合です。この場合、数十ページから数百ページ以降は表示されないこともあります。ただし、これが辿れなくて問題になるのはプログラムからアクセスした場合で、ヒューマンアクセスで問題になることはまずないでしょう。

例えば Google の検索結果は 1,630,000 件ヒットしても実際には 500 件程度までしかページングできませんが、それで困る人はまずいません。それと同じです。

残る問題はプログラム過去データを分析したい、というユースケースです。こういった場合には URLブックマークの件数データを、生データを公開するなどで今後、対応していきたいと思っています。プログラムから統計情報を利用したいという限定された利用のために、通常のアクセスの速度を犠牲にするのは、サービス運営上難しいです。そういったトレードオフがあるということをご理解ください。(そもそも、ロボット過去データをクローリングして統計情報として利用するという利用ケースは推奨していません。過剰アクセスは通常のユーザーアクセスに悪影響を与えるので、制限せざるを得ません。新バージョンベータ公開時にサイトがダウンしたことがありましたが、原因はこの機能でした。旧バージョンで動作していたのは、スケールしていたからではなく、たまたまです。旧バージョンでも頻繁に、この機能が原因でサイトがダウンすることがありました。)

過去データの蓄積から検索したりといったことがブックマーク目的ではないかというのは、その通りです。ですので、できうる限り蓄積したデータは提供していますし、検索もできます。

集合知」というのはロングテールデータロボット等で取り出せるように公開しておく、ということだとは思いません。ロングテールにあるデータも含めて、何かしらの仕組みで情報要求通りに順位付けをした上で提示することだと思います。そのために検索や、関連エントリーなど(人気エントリー、新着エントリーといった限定された範囲のデータを対象にしたものではなく)広範囲のデータに対するアプローチがあります。今後もその辺りを継続的に強化していくつもりです。

2008-10-17

List::Rubyish の delete_if 13:54

delete_if ですが、手元には修正済みのコードがありました。すいません。git に上げてなかったです。 > d:id:yappo

sub delete_if {
    my ($self, $code) = @_;
    croak "Argument must be a code" unless ref $code eq 'CODE';
    return $self unless $self->size;
    my $last_index = $self->_last_index;
    for (0..$last_index) {
        my $item = $self->shift;
        local $_ = $item;
        $self->push($item) unless $code->($_);
    }
    return $self;
}
トラックバック - http://naoya.g.hatena.ne.jp/naoya/20081017

2007-09-29

Gearmanのやつ#2 12:59

もう少し深追い。id:naoya:20070928:1190974874 の通り、Gearman::Worker がリクエストを最後まで受信できないのを直すこと自体は sysread() 呼びだし一回で済ませようとしてるところを(無期限ブロックを回避しつつ)ループにするなり後述する MSG_WAITALL を使うなりすればよさそう。

ここから先に調べたのは二点

  • そもそも sysread() 1回で指定したサイズを読めないのは何で?
  • ソケットの受信バッファって結局何とかその周辺

というところ。以下、うんちくです。

sysread 1回で指定したサイズを読めないのは

「ソケットからの read(2) が、その指定したサイズどおりに読めないことがあってそこは不定」というのは FAQ だけども、じゃあ具体的にどういうときに指定したサイズどおりに読めないのか。ちゃんと説明できないなあ、ということで、深追い。

最初は id:naoya:20070928:1190974874UNIXネットワークプログラミング〈Vol.1〉ネットワークAPI:ソケットとXTI から引用した箇所にもあるとおり、

  • ソケットの受信バッファが溢れたとき(とあと例外時)に限り一回で読めない

という風に思ってたのだけど、それだけじゃないですね。もちろんソケットの受信バッファが溢れたときもそうなる(その場合 TCP の流量制御でクライアント側も送信で待たされる) んだけど、

TCP socket ではデータは単にバイトストリームとして扱われるので、例えば送り側が 2048 Bytes を 1 回の send() で送ったからと言って、受信側が 1 回の recv() で受け取れる保証はない。

転送の都合で send したデータが適宜切り分けられ、複数のパケットで送られる場合がある。そして recv() は「読め」と言われたバイト数が溜るまで待たず、少しでもデータが送られてくれば return する。相手が何バイト送ったかなどをチェックするようにはなっていないということである。

一般的には、これから送るデータのサイズを通知して、受け手の方でこのサイズを受け取るまで繰り返して recv() するという方法がとられるだろう。また、fdopen(sock) しておいて、fread() を使うという手もある。

プログラミング系 Q&A 2

ということでした。

もうひとつ Perlネットワークプログラミング―ソケットの使い方からクライアント/サーバーシステムの開発まで の P.22 から引用すると

sys* () 関数による stdio のバイパスには、実際のデータ量が要求されたデータ量に満たなかった場合に、read() 関数と sysread() 関数で振る舞いが異なるという効果もある。read() 関数の場合には、リクエストされたデータを正確に取得できるまで、関数は無期限にブロックされる。唯一の例外は、ファイルハンドルリクエストを完全に満たす前にファイルの終端に達した場合である。この場合、read() 関数ファイルの終端までのデータを返す。対照的に、sysread() 関数では部分的な読み取りが可能である。この関数は、リクエストされたデータをすぐに読み取れない場合、その時点で取得できるデータを返す。データがまったくない場合、sysread() 関数は最低でも1バイトを読み取るまでブロックされる。このため、sysread() 関数は、データが不確定なサイズで送信されることが多いネットワーク通信に欠かせない存在である。

とある。(上記は Perl の read() / sysread() の差異の話。sysread は C の read(2) に相当)

この二点を合わせると

という動きになる。

まとめると sysread() (もしくは read(2)) が指定したサイズ通りに帰らない条件は

の 5 つ。

ループで何度か read(2) を発行する意外に、この 5 つの条件を 3 つに減らす方法、つまり例外時以外は指定したバイト数分読むまでブロックすることもできて、recv(2) で MSG_WAITALL 使うなどがあるそう。(via UNP)

例えば Gearman::Util::reas_res_packet() の sysread のところを

        my $rv = recv($sock, $buf, $len, MSG_WAITALL);
        die $! if not defined $rv;
        my $buflen = length $buf;
        return $err->("short_body") unless $buflen == $len;        

と変更すると、件の問題はひとまず発生しなくなる。(がこの実装だと $len が意図しない値だった場合に無期限ブロックしてしまうのでその対処は必要です。)

ソケットの受信バッファって結局何とかその周辺

ソケットの受信バッファは setsockopt / getsockopt で SOL_SOCKET, SO_RECVBUF で見たりセットしたりできる。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>

int main ()
{
    int val;
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    int len = sizeof(val);

    if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, &len) == -1 ) {
        perror("failed to getsockopt");
        exit(1);
    }

    printf("%d\n", val);
    return 0;
}

こんな具合で。

% ./rcvbuf_size
87380

この 87380 という値は Linux の場合

% cat /proc/sys/net/ipv4/tcp_rmem
4096    87380   174760

と、ここで決まる。真ん中がデフォルトの値。TCP の流量制御の中でこの値は OS自動デフォルト値から減らしたり増やしたり調整をするようです。

この SO_RCVBUF の値がいつどういう風に使われるのか。TCP パケットの「Window フィールド」に埋め込まれて、パケットやりとりの中でサーバー / クライアントで交換されます。これでお互いに、相手にはあとどれぐらいの受信バッファが残っていてどのぐらい送れるか、というのが調整される。これ以上相手には送れない、という状態になったら送り手側はそこでブロック。これが TCPウィンドウ通知の機能。

この Window フィールドhttp://www.fuwafuwa.org/Lecture/learn/network_nepc/course2/chapter04/section05.html などを見てもわかるとおり 16 ビットしかありません。とういことは普通整数値をやりとりするのでは 65,535 バイトまでしか表現できない。そこでウィンドウスケール。TCP 接続確認時の ACK パケットに Options フィールドに3バイトデータ

を埋め込んで送りつけてやると、バイナリシフトカウンタで指定した値 (0 ~ 14) に従って Window サイズをビットシフトして計算するようになる。これで 65,535 x 2^14 の SO_RCVBUF の値をやりとりできるようになります。ウィンドウスケールに関しては TCPのしくみと実装―RFCの詳細から実装系の解析まで (TCPIP基礎シリーズ) が詳しかった。

ところで、このウィンドウスケールは TCP の 3ウェイハンドシェイクの時点で何ビットシフトするかを決定します。ということは、setsockopt(2) で明示的に SO_RECVBUF に 16 ビット以上の値を指定したい場合、SYN セグメントを飛ばす前 = connect する前ににセットする必要がある、とのことです。(ただ、Linux の場合はデフォルトで 65,535 以上の値を使ってるので、特に意識しなくてもバイナリシフトカウンタに正の値が指定されて3ウェイハンドシェイクが始まってるんではないかと思う。)

更に、SO_RECVBUF に指定できる値は Linux の場合その上限が

% cat /proc/sys/net/core/rmem_max
131071

で制限されています。また、ウィンドウスケールの有効/無効自体は

% cat /proc/sys/net/ipv4/tcp_window_scaling
1

と、ここで決まる。

最初はソケットの受信バッファ周りを疑っていて、そんで調べていたらこの辺にたどり着きました。せっかく調べたのでこちらもまとめてみました。

ということで

Gearman::Util のパッチを、と思ったけど Gearman 全体の処理の中でタイムアウトとかパケットサイズの上限をどう実装すべきか、というのが。

hideokihideoki2008/01/26 15:03てすと

stanakastanaka2008/01/26 15:04テスト2

この日記のはてなブックマーク数