mongodb 2系と3系でpropertyの並び順が変わった

mongodbを2.4から3.2にアップグレードする作業を行っている。3.2にするとテストが大量に失敗するようになったのだが、その原因の一つがこれ。

2.4

> db.Test.update({_id: "nyan"}, {$set: {hoge: {b: {x: 1}}}}, {upsert: true});
> db.Test.findAndModify({query: {_id: "nyan"}, update: {$set: {'hoge.a': {x: 1}}}, new: true});
{
    "_id" : "nyan",
    "hoge" : {
        "a" : {
            "x" : 1
        },
        "b" : {
            "x" : 1
        }
    }
}

3.2

mongos> db.Test.update({_id: "nyan"}, {$set: {hoge: {b: {x: 1}}}}, {upsert: true});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
mongos> db.Test.findAndModify({query: {_id: "nyan"}, update: {$set: {'hoge.a': {x: 1}}}, new: true});
{
    "_id" : "nyan",
    "hoge" : {
        "b" : {
            "x" : 1
        },
        "a" : {
            "x" : 1
        }
    }
}

2.4はsetした順番に関わらずa→bとなるが、3.2はsetした順番に並んでいる。アプリケーションコードでオブジェクトを配列に変換しており、その配列要素の順番が変わったためテストが失敗するようになった。

mongo的にもJS的にもプロパティの順番は仕様で決まっていないので順番に依存すべきではない。今回はアプリ側では配列要素の位置が入れ替わっても問題なく、テストコードだけが順番に依存していたのでテストを直すだけにした。

npm 2.15ではdevtoolsetのgccでビルドできません

Node.js v4系のネイティブモジュールをビルドするにはC++11対応のコンパイラが必要です。

CentOS 6のgccは4.4と古くてビルドすることができないのですが、devtoolsetでgcc 4.8を入れることでネイティブモジュールを npm install できるようになります。

sudo wget http://people.centos.org/tru/devtools-2/devtools-2.repo -O /etc/yum.repos.d/devtools-2.repo
sudo yum install scl-utils
sudo yum install devtoolset-2-gcc devtoolset-2-gcc-c++ devtoolset-2-binutils
scl enable devtoolset-2 -- npm i

しかし、Node.js v4.6に付属しているnpm 2.15ではdevtoolsetを設定しても、gcc 4.4でビルドされてエラーになってしまうのでした。

$ npm -v
2.14.12
$ npm run nyan

> test@1.0.0 nyan /home/nullpon/test
> echo $PATH

/usr/lib/node_modules/npm/bin/node-gyp-bin:/home/nullpon/test/node_modules/.bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/nullpon/bin
$ npm -v
2.15.9
$ npm run nyan

> test@1.0.0 nyan /home/nullpon/test
> echo $PATH

/usr/lib/node_modules/npm/bin/node-gyp-bin:/home/nullpon/test/node_modules/.bin:/usr/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/nullpon/bin

んー、これはひどい。2.15.9では元々のPATHの前に /usr/bin が付加されてしまっています。どうりで scl enable devtoolset-2gcc 4.8にパスを通しても /usr/bin/gcc が使われてしまうわけだクソッタレ!

なお、この糞挙動はnpmのmasterでは既に修正されていますが、Node.js v4付属のnpm 2にはバックポートされておりません。LTSとは一体何なのか…

応急処置としてnpm 2.14をインストールすればビルドできるようになります。

sudo npm install -g npm@2.14.12

npmをダウングレードしたくない場合は、環境変数CC、CXXを設定すれば良さそうです。

export CC=/opt/rh/devtoolset-2/root/usr/bin/gcc
export CXX=/opt/rh/devtoolset-2/root/usr/bin/g++
npm i 

もしくはCentOS 6のサーバを窓から投げ捨てて、CentOS 7にするのも良いでしょう。あとはとっととNode v6に上げるとか。v6付属のnpm 3なら修正済みかもしれません。知らんけど。