BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース 10gen: MongoDBのフォールトトレラントの正当性を主張

10gen: MongoDBのフォールトトレラントの正当性を主張

原文(投稿日:2013/02/07)へのリンク

 

コーネル大学の教授が,MongoDBのフォールトトレランスシステムに "設計上の欠陥がある" と主張している。10genでは,同社のテクニカルディレクタがこれに対して反論する。

コーネル大学 准教授の Emin Gün Sirer氏は,ACIDトランザクションをサポートするキー・バリュー・データストアである HyperDex の開発者だ。その氏が先日,Broken by Design: MongoDB Fault Tolerance と題したブログ記事で,MongoDBのフォールトトレラントシステムについて批判した。Sirer氏が大きな問題として取り上げているのは,MongoDBがフォールトトレランスのために,複数のノードに対してデータのレプリケーションを書き込む方法に関するものだ。氏は "MongoDBにおいて,書き込み(あるいは挿入)が完了したというのはどの時点を意味するのでしょうか?" という問題を読者に出して,次の3つの中から選択するように求めている。

  1. すべてのレプリカへの書き込みが完了したとき。
  2. レプリカのどれかひとつに書き込みが完了したとき。
  3. レプリカの大半への書き込みが完了したとき。

Sirer氏によれば,実はどれも正解ではない。

答は上記のいずれでもありません。MongoDB v2.0で書き込み処理を完了,つまり finito とみなすのは,クライアント機のソケットバッファにデータがバッファされた直後 なのです。この文章をよく読んでみてください。つまり,こういうことなのです。

データがまだフロントエンドのマシンを出ていない,ネットワークにひとつのパケットも送信されていない,そんな状態をMongoDBでは,データが安全にコミットされた,としているのです。リビングルームの郵便物の山に埋もれたままの状態で,小切手は先週発送した,と言い張っているようなものです。

特にハードウェア障害の場合,データはもはや複製されなくなるので,問題は極めて重大だ。

Sirer氏は他に "最後の書き込みが伝搬されたレプリカの数" をチェックするgetLastError()というAPIコールに関しても,いくつか言及している。

  • 処理速度が遅い
  • パイプライン化されていない
  • マルチスレッドで動作しない

氏はさらに,Write Concerns(書き込み確認) の機能上の問題も指摘している。結論として,

ですから,MongoDBは破綻している,それも表面的な問題ではなく,設計面から壊れているのです。MongoDBのフォールトトレランスを信頼しているならば,おそらくそれは間違ったことをしています。

MongoDBに対する批判は,これが初めてではない (InfoQ の記事 "MongoDBの信頼性に疑問" を参照)。

公正を期すためにInfoQでは,MongoDBをサポートしている10genにもコンタクトを取った。Sirer氏の指摘した5つの問題点に回答してくれたのは,同社テクニカルディレクタの Jared Rosoff氏だ。

Jared Rosoff: これらの疑問はすべて,MongoDBのWrite Concern機能に関するものです。ですから,記事にある個々の指摘を取り上げる前に,まずWrite Concernの働きについて説明しましょう。Write Concernsは挿入,更新,削除といったデータベースへの書き込み処理に適用されるエラーチェックとレポートのレベルを,開発者が指定するためのメカニズムです。Write Concernでは,データベースとドライバに対して,複数の待ち条件をコントロールしています。サポートされているのは以下のレベルです。

  • Errors Ignored – エラーチェックはまったく行われません。このオプションは,通常の運用では使用しないでください。
  • Unacknowledged – クライアント上でエラーチェックを実施します。ネットワーク接続などのエラーを捕捉しますが,サーバの応答待ちは行いません。
  • Receipt Acknowledged (デフォルトモード) – クライアントは,データベースサーバが書き込み処理を実行するまで待機します。要求処理中に発生したクライアント,ネットワーク,あるいはサーバ上のエラーがドライバに通知されます。
  • Journaled – サーバディスク上の先行書き込みログ(Write Ahead Log)への書き込みが実行されるまで,クライアントは待機します。ジャーナルは100ms周期でディスクに書き込まれています。従ってこのオプションを使用した場合には,書き込み完了が通知されるまで,最大100msの遅延が発生する可能性があります。

Write Concernレベルとして Receipt Acknowledged または Journaled を指定した場合には,ドライバのレプリケーションファクタも指定することができます。これは書き込み確認を送信する前に,いくつの書き込みレプリカが存在していなければならないか,という数値です。レプリケーションファクタの指定は,存在する必要のある数を示す整数値か "majority" - レプリカセットに属するサーバの過半数(majority),またはサーバのエラーモード名を示す文字列によって行います。以上の情報を背景に,氏がブログ記事で提起した問題点について検討してみたいと思います。

問題点1:MongoDBのデータ書き込み成功報告は嘘である

Jared Rosoff: これは単に誤りです。MongoDBの書き込み通知は,Write Concernで指定した通りに動作します。ブログ記事で指摘されているシナリオは,Write ConcernレベルとしてUnacknowledgeを指定した場合のものです。これは開発者が,書き込み送信時を完了と見なすように明示的に選択したときに限られます。

以前のバージョンのドライバでは,デフォルトの動作がUnacknowledgedになっていました。これは開発者が自身のニーズに合わせて,より適切なWrite Concernを選択するように期待した上での設定でした。ところが,多くの開発者がデフォルト値をそのまま使用したため,混乱や問題を起こすことになってしまいました。ですから私たちは,デフォルト値を変更することにしたのです (http://blog.mongodb.org/post/36666163412/introducing-mongoclient )。

現在,オフィシャルなMongoDBドライバのデフォルト動作は Receipt Acknowledgedになっています。つまり,サーバが書き込み処理を完了して,クライアントに制御が戻るまで待機するようになります。

問題点2: getLastErrorを使用すると書き込み処理が遅くなる

Jared Rosoff: getLastErrorはMongoDBプロトコルの基本コマンドで,Write Concernの実装に使用されています (http://docs.mongodb.org/manual/reference/command/getLastError/)。直感的に言って,サーバの処理完了を待ち合わせる処理は,待ちを行わない場合に比べれば遅くなります。

問題点3: getLastErrorの動作がパイプライン化されていない

Jared Rosoff: アプリケーションによって,要求する動作は違うかも知れません。例えば,複数の挿入 insert(a) insert(b) insert(c) を実行したい,その中のひとつが失敗したらその時点で一連の処理を中止したい,としましょう。この場合,insert毎にWrite Concernを指定することができますが,それによって各insertの後にgetLastErrorが挿入されることになります。ほとんどのバルクロード処理では,複数のinsertに対して定期的にgetLastErrorを挿入する方法は適切な選択と言えます。必要な動作とエラーモードを選択することが,開発者にとって重要なのです。

問題点4: getLastErrorがマルチスレッド動作しない

Jared Rosoff: スレッドが他のスレッドの操作に対する getLastErrorの応答を確認することはありません。MongoDBのgetLastErrorコマンドは,データベース コネクション 上で実行された直前の操作に対して動作します。単純に,最後に実行された任意の処理を対象としているのではありません。オフィシャルなMongoDBドライバはすべて,マルチスレッド環境でもgetLastErrorを正しく処理可能なメカニズムを提供しています。簡単な操作 (WriteConcernを指定したinsertなど) については,ドライバは,insertで使用したコネクションがコネクションプールに返却される前に,同じコネクションを使用してgetLastErrorメッセージを送信します。操作が複雑な場合は,ドライバがrequestStart()とrequestDone()APIコマンドを提供します。この2つはコネクションをそのスレッドにバインドして,クライアント動作中のすべての操作とgetLastErrorコマンドが,同一のコネクション上で送信されることを保証するものです。例として,Java ドライバの並行処理に関する資料を参照してください(http://docs.mongodb.org/ecosystem/drivers/java-concurrency/)。

問題点5:Write Concernは破綻している

Jared Rosoff: これまでの項で説明したようにWriteConcernは,データベースに適用する書き込み処理の堅牢性をコントロールする,柔軟なツールセットを提供しています。パフォーマンスとのバランスを取りながら,オペレーション個々に希望する信頼性レベルを指定することが可能です。ただし自分の希望どおりの指定が可能であるということは同時に,データベースに自分が何を望むのか正しく理解するという責任も伴うのです。

 

この記事に星をつける

おすすめ度
スタイル

BT