mongoDBクラスタにrestoreする時の高速化テクニック

developers.cyberagent.co.jp

真新しいmongoDBクラスタにデータをrestoreする時に高速化テクニックとして、メンテナンス開始前にバックアップデータ等を一度restoreしてmongoDBクラスタを暖気しておく方法があります。

mongoDBの大規模なクラスタに巨大なデータをrestoreすると非常に時間がかかります。例えば800GBのdumpデータをrestoreすると丸一日以上かかってしまいますが、流石にメンテナンスで丸一日サービスを止めたくはないでしょう。

mongoDBはinsertしたデータを一旦プライマリシャードに保存し、データが増えると自動的に他のシャードにデータが分散されていくのですが、このチャンク移動の負荷がかなり大きいのです。

そこで事前にバックアップの本番データをdump、新クラスタにrestoreしてチャンク移動の完了を待ち、ドキュメントの保存先のシャードが決定された状態にします。この状態でデータをremove(dropするとシャーディングの情報も消えてしまうのでremove)してrestoreすると、プライマリシャードを経由せず、最初から適切なシャードに分散してinsertされます。これでチャンク移動の負荷がなくなるので restore の所要時間は1/3 〜 1/4程度に短縮されます。

本番稼働中のDBに対して新規コレクションを追加する時も、あらかじめダミーデータを少しずつ入れて暖気しておくと、新機能のリリース直後のDB負荷が下がるのでおすすめです。

JavaScript配列の空要素の話

gfx.hatenablog.com

配列の空要素の問題ですね。容量は確保されてるが値が(undefinedさえも)入っていない状態。

$ node -p -e "new Array(3)"
[ <3 empty items> ]

この配列のlengthは3になるのだが、forEachなどのイテレーション関数では空要素は列挙されない。

$ node -p -e "new Array(3).length"
3
$ node -e "new Array(3).forEach(i => console.log(i))"    # 何も出力されない

リンク先ではArray.fromやスプレッド演算子を使って、undefinedをセットするコードが紹介されているが、自分はこういうときはfillを使っている。

$ node -p -e "new Array(2).fill()"
[ undefined, undefined ]

0 から 9 の値が入った配列をさくっと作りたいときは

$ node -p -e "new Array(10).fill().map((_, i) => i)"
[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

左から右へ流れるようにチェインできるのがfillを使う場合の利点

ちなみに空要素はlengthプロパティを変更しても作れる

$ node
> const a = [];
undefined
> a.length = 5
5
> a
[ <5 empty items> ]
$ node
> const a = [1,2,3,4,5];
undefined
> a.length = 2;
2
> a.length = 5;
5
> a
[ 1, 2, <3 empty items> ]