pandora agentを一般ユーザで起動する

Pandora FMSのagentは普通のユーザでも起動できます。(zabbixは現状zabbixユーザ以外では起動できないみたいです)
しかし、せっかくの起動スクリプトが su するようになっており、rootパスワードを聞かれてしまいます。

そのため、今のユーザとpandoraの起動ユーザが同じ場合はsuをしないように、軽く以下のパッチを当ててみました。

今は /etc/init.d に置いていますが、これを pandora のディレクトリ以下に置いておけばおーけーになりました。

--- pandora_agent_daemon        2012-03-08 12:36:42.886566593 +0900
+++ /etc/init.d/pandora_agent_daemon    2012-03-08 12:35:51.402566934 +0900
@@ -86,7 +86,12 @@
                        echo "Cannot launch again. Aborting."
                        exit 1
                fi
-               su $PANDORA_USER -c "PATH=$PATH nohup $DAEMON $PANDORA_PATH >/dev/null 2>$LOGFILE"

+               if [ $USER = $PANDORA_USER ]
+               then
+                       PATH=$PATH nohup $DAEMON $PANDORA_PATH >/dev/null 2>$LOGFILE &
+               else
+                       su $PANDORA_USER -c "PATH=$PATH nohup $DAEMON $PANDORA_PATH >/dev/null 2>"

+               fi
                sleep 2
                PANDORA_PID=`pidof_pandora`
                echo "Pandora FMS Agent is now running with PID $PANDORA_PID"
@@ -100,7 +105,12 @@
                        exit 1
                else
                        echo "Stopping Pandora Agent."
-                       su $PANDORA_USER -c "kill $PANDORA_PID >/dev/null 2>&1"
+                       if [ $USER = $PANDORA_USER ]
+                       then
+                               kill $PANDORA_PID >/dev/null 2>&1
+                       else
+                               su $PANDORA_USER -c "kill $PANDORA_PID >/dev/null 2>&1"
+                       fi
                fi
        ;;

Pandora FMSを使ってみた

サーバ監視にはnagiosが主流で、最近ではzabbixか、という感じかと思っていましたが、Software Design 2012年3月号で楽天がPandora FMSを使っていると紹介されていました。というわけで、試してみました。

Pandora FMS

Pandora FMSオープンソース(GPL)の監視ツールです。

個人的にいいなと思った特徴は

  • Webインタフェースが綺麗
  • 日本語化されてる
  • agentのインストールが簡単、かつどんなユーザでも実行できる
  • 単体で監視、グラフ表示が行える
  • nagios pluginも使える
  • 外部APIがある(putもgetも可能)

というところです。

インストール

rpmとかいろいろあるのですが、ここ から VMWare 用のイメージを取ってくるのがお試しには一番簡単です。

ただし、直ぐにパスワードは変えましょう。初期は id: admin, pass: pandoraです。

サーバの設定

まず必要なことはタイムゾーンの設定です。

  • OSのtimezone設定
  • Pandoraの言語の設定
  • Pandoraのtimezoneの設定 (Asia/Tokyoに)

あとは「システム管理」->「エージェント管理」->「グループ管理」でグループを作っておくとあとで便利かもしれません。

エージェントのインストール

  • 1. ここ からtar.gzを取ってきます。
  • 2. 展開します
  • 3. cd unix
  • 4. sudo ./pandora_agent_installer --install
    • 任意の場所に入れる場合: sudo ./pandora_agent_installer --install /path/to/somewhere

ただ、起動スクリプトにバグがありこのパッチ がないと立ち上がりませんでした。

なお、agentの中身はperlスクリプトですので、基本的にまるごとコピーで動きます。
perlもかなり依存性がないように作られてるので、多くの場合はそのまま動くのではないでしょうか

エージェントの設定

/path/to/somewhere/etc/pandora_agent.conf をいじります。と言ってもお試しであれば

  • server_ip 192.168.0.10

を変更するだけでいいのではないでしょうか。必要であれば

  • autotime 1

を加えて、agentではなくサーバ側のtimezoneをみるようにするといいかもしれません。

これで

% sudo /etc/init.d/pandora_daemon start

とすれば、pandora agentが立ち上がり、自動的にserverにデータを送るようになります。数秒待てばserver側にagentの情報が出てくると思います。

PostgreSQL Conference 2012-02-24 at 品川

午前中、ustで流れていた部分の生ログです。残念ながらちょっと遅れてしまったので、基調講演の最初の方は取れていません。

基調講演 by Philippe BEAUDOIN, @ Bull.net

CNAF PostgreSQL Project

フランスの社会保障省。

  • 11ミリオンの家族と30ミリオンの人
  • 11億€
  • フランス全土に123 CAF(Local organization)
以前

cobolメインフレームからPostgreSQLに移行した。

全部で18ヶ月、準備に9ヶ月。

(snip)...

migration

一番大きいDBが250GB。migrateとチェックを合わせて24時間以内にできた。 全部で4TB。

test

unit testが重かったが、受け入れテストのほうが重かった。

バッチを一ヶ月連続で動かした。毎日シーケンスを動かした。 一日のトランザクションを記録しておいて、複数回再実行した。 結果が同じかどうか確認した。

PostgreSQL instance

各CAFの中に、168のDBがある。10個のパーティションに分けた。

メモリ使用関連、特にキャッシュバッファの設定が一番難しかった。 キャッシュバッファの使い方のモデルを作った。

管理ツール

168個とDBが多い。ツールを使って管理した。

開発者はpgAdminを使っている。テスターとサポートはphpPgAdminを使ってい る。

phpPgAdminは各DBにアクセスするための一意なアクセスポイントをwebサーバを 使って提供出来るところがいいところ。DBの管理が楽になる。

保存はpgdumpは使っていない。legato networkerというソフトを使っている。 毎月indexの再生成のために、clusterというコマンドを動かしている。

SQL関連のqueryはpsqlのシートにあって、そのSQLを自動的に呼び出している。

Monitoringはnagios(check_progress.pl)。CAFとつなぐDBSP linkの監視にも nagiosを使っている。

Batch Chains
可用性

ミラーを毎日作ってる。バッチになにか問題あれば、postgresのインスタンス を一時止めて前日のデータを使う。

ミラーは二つある。2つ目のミラーはDisaster Backup Centerにある。

性能

以前のDBよりpgsqlは効率的だった。

既存システムとpgsqlのパーティション間を繋げるのにコストがかかる。つまり、 ユーザ的にはコネクションの処理時間が少し上がった。

バッチの処理時間が結構下がった。一方、いくつかの大きなプログラムのバッ チは時間がかかるようになった。これは、並行runで解決した。

各CAFの中で使っているSQLは簡単。joinもなく、ほぼ一つのテーブルだけ。だ いたい1日で10億query。最も負荷が高い時は、 33000 query/sec 参照されてるデータはほとんど大きくないが、queryが多いのが大変。

移行の際のアドバイス
  • プロトタイプを作ること。リスクを減らせる
  • プロジェクト管理の技術を使うこと
  • PostgreSQLのインフラ設定を注意深くすること
  • 移行はアプリ本体だけでなく、外部ツールに対しても影響があるので注意
  • テストテストテスト
顧客の感想
  • すべての関係者はPostgreSQLに感謝している
  • プロジェクトは時間通りに進んだ
  • PostgreSQLそのものの問題は 0 だった
  • 現在進行中の課題
  • CAFは毎月3億のユーロの支払いを行なっている。これにPostgreSQLが使われている
Q and A
  • Q: なぜPostgreSQLを選んだか
    • :A: 発表すると、一つ目の質問はいつもこれだ。いい質問です。bullから提:案された。bullは以前のDBシステムにも絡んでいて、Oracleとpgsqlに 移行が可能だった。で、顧客は以前oracleを使ったことがあってあんま りよくなかった。 提案段階で、顧客からのテストにも耐えた
  • Q: 多くの人が関係したが、彼らはpostgresに習熟していたのか、教育はどう やったのか
    • A: 顧客はPostgreSQLを結構分かっていた。だからあまりトレーニングはし なかった。
  • Q: スライド17ページ。クローン1と2の同期方式は?バッチだと思うが。
    • A: EMSという会社が作ったTime Finderというプログラムを使った。 変更は特に必要なかった。
    • Q: ハードウェア技術でのクローニング?
    • A: はい。ハードウェア的なクローニング。PostgreSQLからは透過的。 リカバリ時にはPostgreSQLの再起動が必要。PostgreSQLからはディスクの クラッシュに見える。
  • Q: プロジェクト期間中どれぐらいのエンジニアが働いていたのか
    • A: 18ヶ月100%で働いていたのは8人か10人ぐらい。
    • Q: 少ないので驚いているが、それで動いたんですね。とても印象的です。



PostgreSQL 9.2の概要 by Robert Haas @ enterprisedb.com

9.2 opens New Horizons

スケーラビリティが肝。

  • high-endサーバー
  • より大規模なデータセット
  • PostgrSQLのより多くのコピー
  • より多くのレプリカ
9.2の目玉機能
  • スケーラビリティ
    • たくさんのCPUを持つサーバ向け
  • index only scan
  • 省消費電力
  • 新しいバックアップとレプリカのオプション
    • スケールアウト向け
Scalability

CPUが多ければもっと大量のトランザクションをこなせる。

9.1まではロックがあるため8コアぐらいまでしか性能が出なかった。

9.2では32コアまで増やせる。

9月に取ったグラフを見ると、最新のバージョンだと fast lockのパッチを入れ た時点よりもっと性能が向上している。Read Onlyで32コアのマシン。32クライ アントでも225000 query/sec出ている。その後、80clientでも性能劣化が少な い。

pgdump -S、scale factor 100、32コア、AMD opteron 6128、max_connections = 100、shared_buffer = 8GB。

write scalabilityも、9.1より32、48クライアントでも性能が向上している。

スケーラビリティの向上

いくつものパッチによって向上している。

  • "Fast path"ロック。仮想transaction lockと"weak" relation lockが衝突す ることはほとんどない。だからメインのロック管理をバイパスできる
  • MBCC スナップショットを取るときのクリティカルセクションを短くした。多 くアクセスされるデータは別の配列に分割しすることでキャシュライン passingを減らすことで、短く出来た
  • WAL書き出しのスケーラビリティを向上した。複数のバックエンドがWALを書 きだそうとする時にロックをするが、それを減らした。グループコミットの 性能を改善した。TPSで5倍以上改善した。
  • 並行WAL書き出し。複数のバックエンドがWALストリームに並列で書き込める 現実のアプリは非同期に書き込むので、これによって性能が向上できる。

ほかにもさまざまな改善をしているよ。

Index-Only Scan

9.1まではすべてのindex accessはどのタプルが見えるかどうかをチェックする ために、テーブルをアクセスする必要があった。キャッシュの中にテーブルと indexの両方が入っていないとパフォーマンスが落ちてしまっていた。

9.2では、必要なカラムにindexが貼ってあってタプルが"ALL VISIBLE"であれば、 テーブルアクセスを省ける。多くのケースで、indexだけでqueryを返してテー ブルにアクセスする必要がなくなる。

これを実現するために、"visibility map"と呼ばれる8.4からあった構造をデー タベース破損に対して安全にする必要があった。

index only scanはoraclemysqlでは、かなり前から用意されている機能。今 回PostgreSQLに入ったのは喜ばしい。

省消費電力

herokuとかは、何十万ものpgsqlを展開している。そうなると、消費電力が問題 となる。

アイドル状態になっている時に消費電力に効くのが、1秒間に起きている auxiliary processの数。

postgres9.1では11.5 auxiliary process wake-ups/sec。

9.2開発版では、だいたい7.5 auxiliary process wakeups/secとなった。これ からもうちょっと減らす。

ホスティングプロバイダはこれでwakeups transactionを減らせるので、お金 がかからなくなる。

新しいバックアップとレプリカのオプション

バックアップを広範囲に広げられる。

  • カスケードレプリケーション
  • Base Backup from Standby(via pg_basebackup)
  • pg_receivexlog
  • 新しい同期レプリカモード: Remote Write
他の機能
  • JSON data type
  • Range Type
    • PostgreSQLだけの機能。一定のデータに対してより良いモデリングが可能 になる。特に一時的なデータに対して有効
  • Parameterized Paths
    • plannerの改善。planとqueryの種類をもっと持てる
  • Faster sorting
  • Security Barrier Views
    • もっと強固に
  • Rewrite-Free ALTER TABLE .. ALTER TYPE
    • テーブルを書き換えることなく変更できる
で、次は?
  • バッファの置き換えがシングルスレッドだよね
  • Full page writes cause severe throughtput degradation following a checkpoint
    • でも、スライドを作った後に、設定をチューニングするとかなり改善でき た。これも共有する必要あるね。
  • チェックポイントがI/Oのストールを引き起こすよね
  • いくつかのロックはまだある。特に32コア以上の場合。
Q and A
  • Q: スケーラビリティについて。32コアまでなのはなぜか
    • A: 持ってるテスト環境が32コアだから。64コア頂戴よ。
    • Q: 64コアのマシンあるから、global につなげられればテストしてくれる?
    • A: www
  • Q: スケーラビリティについて。NUMAでメモリのローカリティとペナルティ があってなかなか向上しないと思うが。
    • A: HPのiteniumのマシンを使って計測しているが、メモリアクセスのレイ テンシーに対して大きな影響があるとは思っていない。 スピンロックの方がx64マシンの方がひどいことに気がついた。でも、 NUMAかどうかは分からない。
  • Q: Read Scalabilityのグラフ。クライアントが20-32までの時にTPSがすごい 急に増えているのはなぜか
    • A: 知らねwwwwww
  • Q: 省消費電力。wakeupの数が減ったとあるが、どれぐらい実際の消費電力 に影響があったか分かる?
    • A: おれが知ってる消費電力の測定方法はwakeupの数を測ること。 ボルトとかワットとかはハードウェアに依存してるので有効じゃない。 wakeupを減らすとsafe modeになかなか入らないということがなくなるの で、消費電力に影響する。
  • Q: JSONデータタイプ。今はplainなrowとかarray typeだが、将来的にarray in とかそういう機能を作る予定があるか
    • A: 自分もより拡張していきたいと考えているが、PostgreSQLには他にも多 くの問題があるので優先順位をどうつけるかが問題。マネージャがどれを 評価してくれるかな。
  • Q: write scalability。これはsync commitがoffの場合?
    • A: Yes。測定環境のディスクサブシステムを管理出来なかったから。 十分にメモリがあればsync comitをonをしてもいいとは思う。そういうマ シン欲しいな〜。
  • Q: 9.2でsync commitがonの時に性能が上がるか?
    • A: sync commit onでもWAL書き込みの改善があるから、多くの性能向上が 見込める。
  • Q: write scalability。SSDを使うのが今流行り。SSDに特化するという開発 を進めていくことはるか?
    • A: 全然
    • Q: なんでよ
    • A: 確かにSSDはHDDと違う特性はあるが、基本的には大して変わらない。 技術的にはどちらにも効くということも多いので、SSDに特化する必要は ない。もちろん、今後SSDだけに特に効く最適化をすることはあるが、今 のところ特別な最適な方法というのは見つかっていない
  • Q: SSDの使い方についてコメントがある。SSDはHDDより、ランダムアクセス が非常に速い。シーケンシャルアクセスはHDDの方がちょっと速い。だから、 テーブルやindexなどのデータファイルをSSDに保存し、WALをHDDに置いたほ うがいいんじゃないか。 seqとrandomの割合によって、クライテリアを変えていくとか。 そういった小さな努力だけど、積み重ねていくと改善できるのでは。
    • A: このプレゼンの範囲を超えているね。 基本的にその提案には賛成。でも、いろいろな要素がからみ合っている。 総合的に考えていく必要がある。
  • Q: 9.1だとstreaming replicationではスレーブではレプリケーションできな かったが、cascadeだと出来る?何段まで?
    • A: 今はちょっと分からない。
  • Q: 9.2はいつごろリリース?
    • A: それはいつも待ってみないと分からない。でも、9.0と9.1は9月にリリー スされた。9.2もそれぐらいのタイミングで出せればなぁと思っている。 でも、開発者はボランティアなので強制することはできない。

Kindle4

ちょっと前になりますが、Kindle4買いました。アメリカから。まさかその一週間後に日本で展開するという報道があるとは…まあ、Amazon.comのアカウントが移せればいいんですけどね。

写真はSphinxで書きだしたePubファイルをCalibreでmobiに変換してkindleに送ったものです。全然問題ありませんね。

nook touchと同じページですので、比較してみるといいと思います。あんまり変わらないですが、やはり日本語がきちんと出るのはKindleの方です。

さよなら、nook…君もroot取った後にrebootしてちゃんと動いてれいればすばらしかったよ…

Tinkererを使ってbitbucketでblogはじめました

TinkererSphinxを利用して作られたblogツールです。Sphinxを使っていますのでreStructuredText形式で書くことができます。また、blockdiagをはじめとするSphinxの豊富な拡張機能を使うことができます。

今回はtinkererで作成したblogをbitbucketでホスティングしてみます。

bitbucketでのホスティング

bitbucketでは .bitbucket.org という名前のレポジトリを作成し、そこにindex.htmlファイルを置くことでwebページのホスティングができます。これは普通のレポジトリですので、push/pullできますし、履歴管理も出来ます。

そして、実はこのレポジトリはprivateでも構わないのです。privateにすることで、draftなどを隠すことができます。あ、githubと違ってbitbucketはprivateレポジトリが作りたい放題です。

tinkererを実行

さて、実際にblogを書いてみましょう。

その前に、bitbucketで以下のレポジトリを作成します

  • .bitbucket.org (private レポジトリ)

あとはtinkererを実行します

  % mkdir blog
  % cd blog
  % tinker -s  ## ファイルができる
  % vi conf.py
  (タイトルなどを設定する)
  % tinker -p "first post"
  (投稿する)
  % vi 2012/01/04/first_post.rst
  (適当に書く)
  % tinker -b   ## build

これでblog/html以下にhtmlファイルが生成されます。

次に普通にレポジトリを作成しましょう。

  % hg init
  % vi .hg/hgrc

    [paths]
    default=ssh://hg@bitbucket.org/<username>/<username>.bitbucket.org

  % hg add .
  % hg commit -m "first post"
  % hg push

これで .bitbucket.org でtinkererによるblogが出来ました。 http://.bitbucket.org にアクセスしてみてください。

あとは

  • % tinker -p "なにか"

でページを作成・編集してから

  • % tinker -b

でhtmlを生成し、add、commit、pushをすれば更新できます。また、

  • % tinker -d "draft"

でdraftsディレクトリ以下に下書きを作成することも出来ます。

注意点

bitbucketによるホスティングではindexは禁止されているのでdrafts以下は見えません。privateレポジトリなのでレポジトリに他の人がアクセスすることもできません。しかし、直接URLを指定することでファイルの取得ができてしまいます。

そのため、例えばconf.pyなどはほぼ公開と同じようになってしまいます。また、drafts以下のファイルもファイル名が分かってしまうと取得できてしまいます。

実例とbitbucketの注意点

というわけで、英語ブログをはじめました。

http://rrudi.bitbucket.org

ぼくの普段使っているbitbucketのアカウントはr_rudiなのですが、ホスティングする際にこのアンダースコアが問題になってしまいました。URL的にだめみたいです。twitterに投稿しようとしてできなくて悩みました。アンダースコアはRFC 2396ではOKなような気がするのですが、あんまり詳しく調べていません。

追記

blog/html/index.html へのリダイレクトですが、tinker -s で初期設定をtinkererが吐き出した時に一緒にトップディレクトリにblog/html/index.html へのリダイレクトを行うindex.htmlが吐き出されます。

追記その2

conf.pyとかはダウンロードできちゃうよ、って書きましたけど、よく考えればブランチを切ればいいんでした。

  • default
    • 公開用ブランチ。index.htmlとblog以下だけ含まれている
  • drafts
    • 書き用ブランチ。その他のが全部含まれている。

としておいて、普段はdraftsブランチで書いておいて、公開する時にmergegraftすればいいんですね。

Army of Darkness Defense

正月は「いまさらかよ」と言われそうですが、iPhoneアプリのArmy of Darkness Defenseをやってました。いや、かなりおもしろいわこれ。

初回プレイは

  • Boomstick + Catapult
  • Armored Guard + Arthur

でwave 50をクリア。Boomstickは範囲攻撃+再使用までの時間が短いので結構使えます。

次は

  • Magic Words + Deathcoaster
  • Horseman + Arthur + Archer

でクリアした後、EndlessモードでHorsemanをLv Maxまで上げて20体ぐらい出すと終わらなくなるというのを確認しました。

最後に

  • Boomstick + Deathcoaster
  • Peasant + swordboy

でLv50までクリアしました。

実はPeasantはやればできる子で、Lv Max(こいつだけLv11まで上がる)に上げてひたすら生産するだけでLv49までは勝てたりします。最初にちょっとironを貯めておいてwisemanも出しておくとよりいいかもしれません。ただ、Lv50はラスボスの攻撃力が高いのでArthurを出す必要と、Deathcoasterでがつんと削る必要とがありました。

最終的に全52個のarchivementsを全て集めました。いえい。

なぜか最終面だけ異様に難しいとかよくあるんですが、これはそういうこともなく、いろいろな遊び方ができるので非常に楽しめました。オススメ。

multicornを使ってPythonでfdwを実装する

今の会社に入ってからPostgreSQLを使い始めました。というわけで、初心者ですがPostgreSQL アドベントカレンダー 2011に参加してみます。

さて、PostgreSQL 9.1からSQL/MED規格の一部である 外部データラッパ (FDW:Foreign Data Wrapper) がサポートされました。すでに PostgreSQL アドベントカレンダー 12月3日分にて、板垣さんが WebAPIをfdwから叩いてます。この記事では www_fdw という既存のfdwを利用する方法でしたが、今回は実際に自分でfdwを作ってみます。

fdwはAPIを6個程度書くだけで良いのでそれだけでもかなり書きやすいのですが、いかんせんCで書く必要があります。そこで、 multicorn を使ってpythonでfdwを書いてみました。

なにを題材に書こうかなと思ったのですが、Pure Pythonで作られた全文検索エンジンWhoosh を使ってみました。

ソースコードは、こちら に置いてあります。

準備

まず、以下の二つをインストールする必要があります。

  • whoosh
  • multicorn

下にubuntuで叩いたコマンドを書いておきます。

  % sudo atp-get install postgresql-9.1  # postgresql-9.1を入れる
  % sudo apt-get install python-dev  # multicornのcompileにヘッダが必要
  % sudo apt-get install python-pip  # pythonのパッケージ管理ツールpipを入れる

  % sudo pip install pgxnclient  # pgxnを扱うためのクライアント
  % sudo pip install whoosh  # 全文検索エンジン
  % sudo pip install -e "hg+http://bitbucket.org/r_rudi/whooshfdw#egg=whooshfdw"  # 今回実装したパッケージをbitbucketからインストールする

  % sudo pgxn install multicorn --testing  # multicornを入れる

なお、今回はPostgreSQLサーバがpythonを叩くため、パッケージはvirtualenv環境ではなく、システム自体にインストールしてください。

whoosh index作成

全文検索するためにはまずインデックスを作成する必要があります。

registerというクラスを用意しておきましたのでそれを使います。

今回は、wikipediaの全ページのタイトルだけが含まれたファイルを検索してみます。

http://dumps.wikimedia.org/jawiki/20111203/ から jawiki-20111203-all-titles-in-ns0.gz をダウンロードし、/tmpに展開しておいてください。

from whooshfdw import register

register.register(file='/tmp/jawiki-20111203-all-titles-in-ns0', indexdir='/tmp/indexdir')

手元のマシンではだいたい40分ほどかかりました。お試しの場合はhead -n 1000とかしてください。

なお、インデックスですが決め打ちで2-gramで作成してあります。このあたりはコードを読んでくださいね。

SQL

準備が整ったところでいよいよfdwです。まずは Server と Tableを作成します。

CREATE EXTENSION multicorn;  -- multicornをDBにいれる
CREATE SERVER whoosh_srv FOREIGN DATA WRAPPER multicorn options (
        wrapper 'whooshfdw.whooshfdw.WhooshFDW' -- Server作成
);
CREATE FOREIGN TABLE whooshtable( -- FOREIGN TABLE作成
        id numeric,
        title character varying
) server whoosh_srv options (
        indexdir '/tmp/indexdir'  -- インデックスの場所をオプションで指定
);

できたところで SELECT 実行です。

% psql -c "SELECT * from whooshtable where title LIKE '%どこでも%';"
 id |       title        
----+--------------------
  1 | どこでもまど
  2 | どこでもドア
  3 | どこでもパス
  4 | どこでも大砲
  5 | どこでもCHU
  6 | どこでもドアー
  7 | どこでもいっしょ
  8 | どこでもチョコボ
  9 | どこでもWiiの間
 10 | どこでもドラえもん
(10 rows)

ちゃんと返ってきてますね。

実装の解説

multicornを使ったfdwの実装は ForeignDataWrapper クラスを継承したクラスを作成し、コンストラクタである __init__ と実行メソッドである execute を実装するだけです。

__init__ではsuperを呼び出すだけです。また、実際の検索を行うexecuteの中では yield を使って値を次々に返して行くだけです。

下はfdwの機能に絞ったコードの抜粋です。(全部はbitbucketを見てください)

class WhooshFDW(ForeignDataWrapper):
    def __init__(self, options, columns):
        super(WhooshFDW, self).__init__(options, columns)
        self.columns = columns
        self.indexdir = options["indexdir"]  # CREATE TABLE で指定したオプションを持ってくる

    def execute(self, quals, columns):
        for query in quals:  # qualsはWHERE句の中身が入っている
            q = parser.parse(query.value)
            with ix.searcher() as searcher:
                count = 1
                for r in searcher.search(q):
                    res = {'id': count, 'title': r["title"].encode('utf-8')}
                    yield res  # 値を辞書形式で返してあげる
                    count += 1

まとめると、

  • __ini__の中でsuperを呼ぶ
  • executeの中でyieldを使って値を次々に返せるようにする

これだけ。

注意点

なお、qualsというのはWHERE句の中身です。そしてwhere句は*必ず*適用されます。例えば、

def execute(self, quals, columns):
    for i in range(1,20):
        yield(i, "hoge")

としておいて、

SELECT * FROM whooshtable WHERE id = 10;

というSQLを実行した場合、「executeでqualはなにも使ってないんだから1から20まで表示されるだろう」と思いますよね。でもそうではなく、ここでは 10,hoge しか出てきません。

速度は?

実用を求めるならCで書けよな、と思いますが一応。

本体 Mac Mini (mid 2011)
OS Ubuntu on Virtualbox
CPU 2.4GHz(Core 2 Duo)
メモリ 8G
postgres 9.1

PostgreSQLの設定はubuntuのそのまんまです。っていうか、VirtualBoxという時点で評価の意味がないような気がしますが…。

1247130行のwikipediaのタイトルだけを抜き出したものでindexを作成するのに

  % time python register.py
  2571.88s user 289.96s system 91% cpu 51:54.10 total

2571秒はだいたい42分。これをfdw経由で検索すると、

 Foreign Scan on whooshtable  (cost=10.00..15.00 rows=9999999 width=64) (actual time=619.132..620.073 rows=10 loops=1)
   Filter: ((title)::text ~~ '%どこでも%'::text)
   Foreign multicorn: multicorn
   Foreign multicorn cost: 10
 Total runtime: 681.000 ms

でした。ちなみに普通にpythonで検索してみました。timeitを使って、100回実行して平均を出してみると、

  % python search.py
  15.917749 msec

おやまあ、やっぱりPostgreSQLからpythonを呼び出すコストが結構かかるようですね。pypyでも試してみたくなりますが止めておきます。

まとめ

今回は PostgreSQL 9.1から搭載されたfdwをPythonで実装できるmulticornを使って、pure python全文検索エンジンwhooshをSQLから叩けるようにしてみました。

…字で書くとなんかむちゃくちゃですね。でも、やったことは非常に少しなのでPostgreSQLの拡張性すごい!ということで。

明日は DaiMotoh さん、お願いします。