Twitter の検索結果を保管しておいてくれるアプリケーション

http://d.hatena.ne.jp/yssk22/20100102#1262278416 の続きです。ひとまず、最低限は動くようになったので我が家にデプロイしておきました。

http://www.yssk22.info/tstore/_design/search/_show/top/

デモ用途なので、結果が飛んだらごめんなさい、ですが、ご自由にどうぞ。キャッシュサービスでしかないので、ユーザー登録/twitter OAuthなどは一切不要です。40GBぐらいは保存できます。

(追記) 半日動かしておいたのですが、キーワード13に対して、Tweetの数が8200 がキャッシュされて、CouchDBのストレージを10M消費。短い単語いれちゃうと、やはりがしがしキャッシュされちゃいますね。。とりあえず様子をみて、パーティショニングするかどうか考えようかと。にっちもさっちもいかなくなればEC2行きで。

何ができるの?

流行り廃りに関係なく自分の好きなキーワードが登録できます。キーワードを登録しておくとあなたの代わりに検索結果のログを蓄積しておいてくれます。これで、ウォッチしているキーワードなどを登録しておくとよいかと思います。尚、大文字小文字区別して登録できますが、twitter側の検索は区別しないようです。tweetが重複して保管されることになるかと思いますが、ひとまず放置で。

また、そのうちサイト内に蓄えたtweetに対して、検索エンジンを仕込んでおこうかと思います

使い方

右上のテキストボックスでキーワードをいれると、CouchDBに保管されている検索キーワードのページを表示します()。まだ登録されていなければ作ることができます。で、この検索キーワードのページを作っておくと裏側でせっせこせっせこ http://search.twitter.com/search.json にアクセスして、検索結果を取得してCouchDB上に保管しておいてくれるのです。

前のエントリでも書きましたが、twitter は Search API でも一定期間?経過した検索結果は見られなくなるようで、これだと困ることがあるので、「そこでCouchDB」というわけです。

ソース

github.com にソースはおいといたので、デスクトップにインストールするのもいいかと思います。MacUbuntuでは動くはず。Windows でも Python 2.6が動けば動くと思います。

http://github.com/yssk22/tstore/

CSSとか全然書いていないのと、トップページが実はまだ。あとライセンスも。。MITにすると思いますが。

ポイント1. External API をマルチスレッド化

CouchDBPython以外はアプリケーションサーバーもcronも不要です。 "cron 不要" にするのがちょっとめんどくさくて、ひさびさ、マルチスレッドのキューアプリ書きました。

2つの種類のスレッドあって、CouchDBのビューにアクセスして、クロールするキーワードを拾ってくる管理スレッドが1つ、そして、管理スレッドは、N個(設定可能)のクローラースレッドを立ち上げておいて、キーワードをクローラースレッドにどんどこ渡していきます。クローラースレッドは search.twitter.com にアクセスして結果を拾ってCouchDBに送り込むだけです。

この2種類のスレッドをたたき起こすのに、External APIでたたき起こすようにしています。ただし、External APIは1リクエストに対して、シングルプロセスでしか処理できないので、プロセスの中で管理スレッドを立ち上げて処理するようにしています。所詮、単にErlangからリンクされる1プロセスでしかないので、スレッド叩いちゃだめだとかそういったものはないはずです。で、External APIにも負荷がかからないのように、管理スレッドの実行状態を定期的に ts-search-crawer-status というドキュメントに保存しておいて、ブラウザからアクセスしたときにこのドキュメントのタイムスタンプが古すぎる(デフォルトは10分)の場合は、External APIにアクセスしてたたき起こすようにしています。こうすると CouchDB が再起動したり、Pythonのプロセスが何らかの原因で消滅したりしても、(CouchDBがExternalプロセスの消滅を検知していれば/この辺はErlangの信頼性) 自動的にクローラーが再開するようになります。

ポイント2. 長時間トランザクション排他制御

管理スレッドとクローラースレッドの間も、ドキュメントのタイムスタンプでクローラージョブを実行制御しています。検索キーワードは "ts-search-検索語" というidで管理していますが、これが、最後にクロールの実行状態と最後のクロール時間を記憶しています。

クローラーは、クロールの開始時に、"ts-search-検索語" の実行状態を EXEC に更新します。この更新が成功したスレッドのみ twitter.com へのアクセスおよび 検索結果をCouchDBに放り込む作業が許可されます。EXEC に更新する = ロックをかける に相当します。長時間トランザクションになるので、これでいいかと。このロックをかける操作はCouchDBによって一貫性が保証されます。

で、クロールして検索結果を放り込み終わったら、SUCC (or FAIL) に戻します。長時間トランザクションのロックなので、たまにEXECのまま放置されることがあるかもしれません。このタイムアウトはまだ実装していません。

ああ、こうしてみると簡易ワークフローで、排他制御をしています。というよりは、CouchDBのビューをキューのように使っているのですが。

ポイント3. Python

素人がPythonを本格的?に書いたので、Queue モジュールの使い方とかあってるのかどうか。。。couchapp に引きずられる形で Python にしただけです。