中年engineerの独り言 - crumbjp

LinuxとApacheの憂鬱

MongoDB and PHP の問題

まず一言!

mongo-php-driver はマトモに動かん!!
(安心して使えるバージョンがない)

  やれやれ・・・

PHPじゃMongoDBが使えない』ってんじゃ寂しいのでちまちま直して使っています。

バージョン別のドライバの状態とパッチ

1.2.0 以前

数々の致命的な問題があるのでお勧めできません。
もし利用しているならば、なる早でアップデートする事を強くおすすめします

特にコネクションプール周りがダメダメで
折角MongoDBを利用してるのに対障害性が全く期待出来なくなる可能性が高い。

1.2.2

初期の1.2系では比較的安定しているバージョンです。

しかし2つの大きなバグがあるのでこれ等のパッチを当ててください。
ソケットディスクリプタ・リーク
  https://github.com/crumbjp/Personal/blob/master/mongo-php-driver/mongo1.2.2.sock-leak.patch
無駄なスリープがあり1/10程の速度になってしまう
  https://github.com/crumbjp/Personal/blob/master/mongo-php-driver/mongo1.2.2.non-wait.patch

これらは既にパッチを送ってあり、1.2.4にて取り込まれています。
但し1.2.3での変更点になんらかの問題があり安定しないようです。*1

わざわざパッチを当てるのが面倒な人はこちらを使ってみてくださいな。
https://github.com/cockatoo-org/mongo-php-driver/tree/1.2.2a

更なるバグ

最近、このバージョンにはSlaveOk時の挙動に問題がある事が判明しました。

MongoDBドライバにSlaveOkを指定した場合は、find / findOneなどのデータ取得メソッドはSECONDARYに投げる事になっています。
ソースコード上もそう記述されているのですが、別のバグの影響でうまく動きません
結果的には一番反応の早いノードに投げてしまい、PRIMARYに投げてしまう事があります。

この意図しないPRIMARYへのリクエストは、MapReduceやその他PRIMARYへのオペレーションと競合します。

結果として事故ると・・・
 MapReduceの負荷によりPRIMARYに向いてしまったオンライン系のfind が刺さってとかとか・・・

しかし、1.2.2を使っている場合、解決方法はありません。

1.2.7

現時点の最新バージョンです。
上記の問題は解決されています。

しかし新たに、致命的なバグが追加されています。
MongoDBのレプリカセットのノードが1つでも落ちた瞬間にPHPがSEGVで落ちる

これはドライバ内リトライ処理中の未初期化ポインタの参照が問題です。

昨日、パッチを作り、PullRequestを送ってありますが、まだマージされていません。

急ぎで利用したい方はココから使ってください。
https://github.com/cockatoo-org/mongo-php-driver/tree/1.2.7b

既に1.2.7を利用している方は、なるべく早く此方を使うことを強くおすすめします。

たまにはソースコードでも!

最速スレーブ探し

  だれでも学ぶ最小探しルーチンですねー
  でも動かないの・・・link->server_set->master=(null)だから・・・
== util/rc.c ==

int mongo_util_rs__set_slave(mongo_link *link, char **errmsg TSRMLS_DC) {
  mongo_server *possible_slave;
  int min_ping = INT_MAX;
      :
   < SKIP >
      :
  while (possible_slave) {
    int ping;

    if (!mongo_util_server_get_readable(possible_slave TSRMLS_CC)) {
      possible_slave = possible_slave->next;
      continue;
    }

    ping = mongo_util_server_get_ping_time(possible_slave TSRMLS_CC);
    if (ping < min_ping && possible_slave != link->server_set->master) {
      link->slave = possible_slave;
      min_ping = ping;
    }

    possible_slave = possible_slave->next;
  }
未初期化君

コネクションプールが死んでた時にプールの中から別のコネクションを拾ってリトライするだけど
mongo_server other; 君は
get_other_connsではother.sock , other.connectedなどの極々一部のメンバしか代入されないのです。
殆どのメンバが未初期化のまま。
最終的には、other.host (char*) がsockaddrに渡されて落ちまする。。
== util/pool.c ==

static void test_other_conns(mongo_server *server, stack_monitor *monitor TSRMLS_DC) {
  mongo_server other;
  zval *response = 0;

  // get another connection
  get_other_conn(server, &other, monitor);

  // if no other connections open, we don't have to worry about closing them
  if (other.connected == 0) {
    return;
  }

*1:既に1.2.2+patchを採用していたので本気検証はしてません。