中年engineerの独り言 - crumbjp

LinuxとApacheの憂鬱

mongosが腐る・・・

mongosの後ろのshardでstepdownが起きたときにmongosが追随せずに以降のクエリーが全て刺さり続けることがある。
こうなるともう自動で復活はしないようだ。


すべてのmongosが腐る訳ではなく、stepdown時に高負荷だったmongosが腐る傾向にある。
shardConnPoolStats コマンドではmasterが変わった事が認識できているので、何かしらのrace condition のバグがあると思われる。

少なくとも2.6系,3.0系で起きる。3.2系は不明だが恐らく起きるだろう。
本番クラスの負荷がかからないと再現しないので原因特定もかなり覚悟が要る。。。


とりあえず、監視スクリプト書いて自動再起動させる事にするが。。

jiraにも上がってなさそうだし、こんなバグ誰も気づかないんだろうか・・・

MongoDBクラスタ間の同期

node-mongosync

https://www.npmjs.com/package/node-mongosync

ステージング環境へのデータ同期や、MongoDB引っ越しの際に便利。
そうそう引っ越さないけど・・

以前 mongoshellで実装したものの焼き直しだ。
node-native-driverでは、tailable cursor の closeが検知出来たり、oplogの読み込みと同期先への書き込みが非同期に同時進行出来るため、性能的に有利だった。
反面CPU処理能力は若干劣るため、フックを差し込む機構は今の所諦めている。

あとnpmを使えるので導入が楽だ。
これが一番大きな理由かもしれない。

npmjs のREADME.md の表示がやたら汚いのがつらい・・・ githubの方みてね・・・

『もう二度と、絶対にMongoDBを使うべきじゃない理由』というのがあるらしい

記事

https://fa-works.com/blog/why-you-should-never-ever-ever-use-mongodb

なかなか香しいな。
というよりコイツ他のブログも結構ヒドイw
とりあえず不満をぶちまけるタイプのようだ。

で、、事の本質はプロダクトの設計がちゃんと出来ない人はどんな場合でも選択を間違えるという事だ。

実際MongoDBは使いどころがかなり限られている。
MongoDBが得意なケース"以外"では絶対MongoDBを使ってはならない
 WriteConcernはあくまでAdditionalな機能であって本来やりたい事では無い。(RDBMSなら2フェーズコミットだぜ?)
 実際、最近追加されたReadConcernもヒドイ実装である。

殆どのケースではORMが完備してるフレームとMySQLを使うのが鉄板なのは間違いない。

じゃあ具体的にいつ使うのか?というと

Dirty read が許され、且つ、Readが支配的な場合

だ。

性能面に関しては、スタンドアローン型のストレージに劣る。
これはレプリケーション機構があるので(例えその機能を切ったとしても)そのオーバーヘッドに拠るものが大きい。但し実装も良くない。


しかしPostgreSQLねぇ・・・運用するんだ・・・がんばれ〜
大丈夫!イザとなったらRedShiftがあるさ。。

MongoDB3.2 readConcernの挙動について

実装

mongosの場合はもうちょっと複雑になるが、レプリカセットの場合はこの辺に実装があるようだ

=== /db/repl/replication_coordinator_impl.cpp ===
    auto loopCondition = [this, isMajorityReadConcern, targetOpTime] {
        return isMajorityReadConcern
            ? !_currentCommittedSnapshot || targetOpTime > _currentCommittedSnapshot->opTime
            : targetOpTime > _getMyLastOptime_inlock();
    };

    while (loopCondition()) {
        Status interruptedStatus = txn->checkForInterruptNoAssert();
        if (!interruptedStatus.isOK()) {
            return ReadConcernResponse(interruptedStatus, Milliseconds(timer.millis()));
        }

        if (_inShutdown) {
            return ReadConcernResponse(Status(ErrorCodes::ShutdownInProgress, "shutting down"),
                                       Milliseconds(timer.millis()));
        }

        stdx::condition_variable condVar;
        WriteConcernOptions writeConcern;
        writeConcern.wMode = WriteConcernOptions::kMajority;

        WaiterInfo waitInfo(isMajorityReadConcern ? &_replicationWaiterList : &_opTimeWaiterList,
                            txn->getOpID(),
                            &targetOpTime,
                            isMajorityReadConcern ? &writeConcern : nullptr,
                            &condVar);

        if (CurOp::get(txn)->isMaxTimeSet()) {
            condVar.wait_for(lock, Microseconds(txn->getRemainingMaxTimeMicros()));
        } else {
            condVar.wait(lock);
        }
    }

どうもmajorityになるまで単純にwhile ループで待ってるようだが
こんな乱暴な事をすると、簡単にDOSになりそうだが、どうなんだろう?

仮に 1秒位遅延が起きた時に、5000 queryも readConcern: majority でクエリーすると何が起きるか?
余裕でリクエストを処理するスレッドを潰されてしまいそうな気がする。

だれか一緒に検証してくれる人いませんかね??

MongoDB3.2 ReleaseNote所感

そろそろ3.2も手を付けようと思うので、検証がてらつらつらと。。

リリースノート

https://docs.mongodb.org/manual/release-notes/3.2/

非常に丁寧な日本語訳があるので、こちらもどうぞ
http://qiita.com/fetaro/items/cd570d70623b58b5deef

WiredTiger as Default

まあ規定路線なので、特に気にする必要なし。

Replication Election Enhancements

Primaryが落ちた時の検知が早くなった。
どういうシチュエーションでどの位早くなるのかはまだ検証してない。
少なくとも今迄の落ち認定10秒をカスタマイズ可能になった。
その他色々timeout値が弄れるようになった様だが、これはまだ実験段階なんだろう。と読む事もできる。
要注目

Sharded Cluster Enhancements

configサーバが独自2-phase-commit ではなく、普通のReplicaSet で組めるようになった。
今迄は3ノード限定。1ノードでも落ちたら(shard周りの)更新不能だったが、可用性が格段に上がったということだ。

readConcern

書き込み時の保証であるwriteConcernだけでなく、読み込み時の保証が取れるようになった。
ただ、こんな事をしてどうやって性能を上げるつもりなのか解らない
これはコードを読む必要がありそう。

Partial Indexes

全ドキュメントにインデックスを張らない随分とチャレンジングな機能。
正にドキュメントベース独特の悩みの具現化だな。
これは機能としては素晴らしいがアプリ側がかなり大変そうな気がする。
限定した使い方なら非常によさそう。

例えば、{ active: true } のドキュメントだけ indexに含める。
といった使い方が良い。
この様なケースならば、Oracleのbitmap indexより効率的かもしれない。

Document Validation

謎機能!!
ドキュメントベースのDBでこれをやるか!!
只でさえフィールドがnull、undefinedで、クエリー周りの混乱があるのにこういう事すると更に拍車をかけそう。
沢山失敗例がネットに溢れると思われる

Aggregation Framework Enhancements

色々足されているが、 まずは$lookupだけ気にしていれば良い。
他は細々しているが

  • 演算系のオペレータは嬉しい。
  • $unwind が配列以外を受け付けなくなったのがちょっと痛い
  • covered indexが有効になったので軽くなるケースも若干ある。(むしろ$lookupで有効ならば非常に嬉しい機能)
MongoDB Tools Enhancements

mongodump / mongorestore が圧縮転送可能になったので、ありがちな、mongodump & s3 に放り込む。といったバックアップが早くなるのが嬉しい。

Text Search Enhancements

例によって無視

New Storage Engines

気にする必要なし

General Enhancements

ビット演算オペレータは使えるケースがあるかな?無いよな・・・
ドキュメントベース使っててフィールド分けない理由が無いんだけど・・・

SpiderMonkey JavaScript Engine

なぜだ!!

WiredTiger and fsyncLock

以前にも触れたが、やっとまともにバックアップが取れるようになる。


あとは良いや。

3.2は新バージョンというより3.0 系の決定版のような位置づけっぽいですね。

AWS上にハイパフォーマンスMongoDBを構築する方法

AWSインスタンスの選定

AWSインスタンスタイプ一覧

以下のインスタンスボリュームが付随しているプロダクトラインが候補になる。
個人的にはi2.xxx が好みである。

r3.xxx
メモリ最適化インスタンス
i2.xxx
SSD容量最適化インスタンス

ただし、インスタンスボリュームはスナップショットが取れず、揮発性なので取り回しが悪い。
Provisioned IOPS (SSD) ボリュームも検討候補になるが、高くつく事になるのでお金持ちの人用。。

バックアップ戦略

流石にバックアップを全く取らずに運用する事は出来ないので
以下様にレプリカセット構成を組み、バックアップを取る。

最後のノードはバックアップ専用なので、SSD以外は非力出よい。
ただしあまり小さいインスタンスを選択するとネットワーク容量が足りなくなるのでそこを考慮して選定する。

構成例

  • i2.4xlarge
  • i2.4xlarge
  • r3.large + 2400GiB EBS Magnet

もちろん負荷に依るが、この様な構成でも成立する。

AWS 周りの必要知識

//docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/i2-instances.html">i2インスタンス:基本使用ボリュームが大きいほどIOPSが高くなる

SSDオーバープロビジョニングの為に、10%ほど未使用領域を残す。激しく使うならi2.2xlarge以上。そうじゃないとネットワークが負ける。

//docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/InstanceStorage.html#InstanceStoreTrimSupport">TRIMサポート:i2 と r3 のインスタンスボリュームは TRIM がサポートされてる。
//docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/storage_expand_partition.html">パーティションファイルシステム設定関連:

手順

1. 普通に AWS console から作る場合


OS領域はマグネットで十分。インスタンスストアを全部追加すること。

2. デバイスを確認
# lsblk
NAME    MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
xvda    202:0    0    40G  0 disk
└─xvda1 202:1    0    40G  0 part /
xvdb    202:16   0 745.2G  0 disk

`xvdb` がインスタンスボリューム

3a. インスタンスボリュームが1つの場合はオーバープロビジョニング用の未使用領域を10%確保

インスタンスボリュームのサイズは800GiB単位で、インスタンスの大きさによって複数のインスタンスボリュームが付随する。
1つだった場合はそのまま使えば良いがSSDの性能を生かす為にオーバープロビジョニングの設定をする。

# gdisk /dev/xvdb

* パーティション(/dev/xvdb1)作成
Command : n
Partition number : 1
FirstSector: 2048
LastSector: XXXX ※ 最後のセクタから10%程余らせた部分を指定する
GUID: 8300

* 名前変更
Command : c
Enter name: Linux

* GUID生成
Command : x
Expert command : g
unique GUID: R

* 確認
Expert command: p
Disk /dev/xvdb: 1562822320 sectors, 745.2 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 4E2273AF-9E75-401A-8D3E-365A4D9674AC
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 1562822286
Partitions will be aligned on 2048-sector boundaries
Total free space is 156284525 sectors (74.5 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048      1406539775   670.7 GiB   8300  Linux

* 反映
Command : w
3b. インスタンスボリュームは2つの場合はRAID0を設定する

本来は各SSDにオーバープロビジョニングを指定した方が良いと思うが、そもそも大容量のストレージを常に使い切りそうな使い方自体がマズいので、運用上、常にある程度の未使用領域が残っている状態が保たれるはず。
そうなると、ある程度TRIMだけで対処できるのではないか?という想像の元にこうしている。
※ 誰か詳しい人が居たら教えてください

`mdadm` が無ければ apt-get install mdadm

インスタンスボリュームが4個の場合

# mdadm --create /dev/md0 --level=0 --raid-devices=4 /dev/xvdb /dev/xvdc /dev/xvdd /dev/xvde

以降、/dev/xvdb1 は /dev/md0 に読み替えてください。

4. ファイルシステムXFSを作る

`mkfs.xfs` が無ければ `apt-get install xfsprogs`

# mkfs.xfs -K /dev/xvdb1
5. マウント

TRIMを有効にする為にdiscardを指定する

# mkdir -p /data
# mount -o discard /dev/xvdb1 /data
== /etc/fstab ==

/dev/xvdb1 /data auto defaults,nobootwait,discard 0 2

# raidしている場合は `blkid /dev/md0`で取得できるuuidを指定する
UUID="**uuid**" /data auto defaults,nobootwait,discard 0 2
6. TRIM が効いてるか確認
# fstrim -v /data/
/data/: 3199094800384 bytes were trimmed
7. ここで一回リブートする
8. MongoDBディレクトリを作る
# mkdir /usr/local/mongodb
# mkdir -p /data/mongo
# ln -sfT /data/mongo /usr/local/mongodb/data
9. limits のnofileを多めに変更

https://docs.mongodb.org/manual/reference/ulimit/

後は普通にMongoDBを構築してください。
リンク先の設定がそのまま使えるはずです。

あまり頻発するような問題ではないようだ

http://d.hatena.ne.jp/hiroppon/20151216/1450251504
以前、MongoDB3.0系のバックアップを取り上げた。

色々検証したが、データファイル自体が破壊されるような事は起きなかった。
中のデータがある程度おかしいのかもしれないし、極稀にファイル破壊が起きるのかもしれないが
あまり大きな問題では無い様な気がする。