「MongoDB がコンテナを食べてしまいました!」

Table of Contents

「MongoDB がコンテナを食べてしまいました!」

Line Break The Registerの毎週のソフトウェア バグ パレード、「Line Break: シーズン 2」へようこそ。

しばらく休止し、何度か休暇を挟みましたが、「Line Break」が再開しました。以前のエピソードはこちらでご覧いただけます。アイデアはシンプルです。もしあなたがバグだらけのコードを見つけて、気が狂いそうになったり、大騒ぎになったりしたら、詳細を添えて私たちにメールを送ってください。あなたの体験談をRegの読者と共有します。これは大きな学びの機会となります。匿名を希望される場合はお知らせください。私たちは常に情報源を保護します。

MongoDB: データベース用のSnapchat

まずは、あなたの謙虚なハッカーが見つけたこの話から始めましょう。Meteorのコア開発者であるDavid Glasser氏が今週、オープンソースのNoSQLの寵児であるMongoDBを使用する際に、プログラマーがデータ競合状態を引き起こす仕組みについて詳しく説明しました。

MongoDBデータベースでエントリを検索すると、結果セットの最初のエントリを指すカーソルが返されます。次に、カーソルを反復処理して、セット内の各結果を1つずつ取得します。ここまでは順調です。ただし、Glasser氏によると、反復処理中に更新が発生した場合、セットに含まれるはずのエントリが、クエリスレッドが参照する前に削除されてしまう可能性があります。

最終結果は? MongoDB クエリは常に一致するすべてのドキュメントを返すわけではありません。

「データベースにクエリを実行する場合、通常はクエリに一致する結果がすべて返されるものと期待します」とグラッサー氏は火曜日のブログに書いた。

最近、MongoDBでは必ずしもそうではないことに気づき、驚きました。具体的には、クエリ実行中にドキュメントが更新された場合、MongoDBはクエリからそのドキュメントを返さないことがあります。たとえ更新前後で一致していてもです!MongoDBをご利用の場合は、この微妙なエッジケースに注意し、クエリがその影響を受けないようにする必要があります。

Glasser氏の場合、彼のコードはプラットフォームのソフトウェアコンテナを記述するメタデータのデータベースを検索していました。このメタデータは、例えばコンテナの状態を「正常」か「異常」かといった情報を提供します。Glasser氏は、奇妙なことに、あるクエリではコンテナが消え、別のクエリでは再び現れることに気づきました。調査の後、彼はMongoDBのドキュメントで以下の記述を見つけました。

読み取り操作の過程で更新された一致するドキュメントが読み取られない場合があります。

言い換えると、次のようなことが起きていました。ソフトウェアはカーソルを反復処理し、コンテナを 1 つずつ移動していました。その間に、コンテナの 1 つが異常な状態から正常な状態に更新され、インデックス内の位置が変更されました。この更新により、変更されたエントリが現在のクエリ結果の位置の前に移動されるため、反復カーソルによってそのエントリが返されることはなく、コンテナは検索結果から消えていました。

飛躍…デビッド・グラッサー氏による問題の図解(クリックして拡大)

「このクエリを 1 日に 10 万回程度実行すると、数日ごとにバグに遭遇します」と Glasser 氏は付け加えた。

教訓:これがロックが有用な理由です。また、他の多くのデータベースパッケージが存在する理由でもあります。上記の競合状態を考慮してソフトウェアを作成するか、別のデータベースシステムを選択してください。もしかしたら、本当に必要なのは緩いNoSQLシステムではなく、SQLデータベースかもしれません。

(ちなみに、MongoDBの戦略担当副社長であるケリー・スターマン氏は次のように述べています。「MongoDBは、ドキュメントの中でデータベースの分離保証について明確に説明しています。私たちが提供する分離レベルに対応したデータモデリングとインデックス作成技術を採用することは可能であり、著者自身もそれが可能だったと述べています。念のため言っておきますが、このユーザーのコンテナは「消費」されておらず、データは失われていません。」)

それはクレイジーな話だ

読者のトムさん(仮にトムと呼ぶことにします)から、スーパーコンピュータセンターでの の誤った使用法に関するお話をいただきましたrsh。 は、通常、リモートマシンのシェルでコマンドを実行するために使用されるツールです。今回のリモートマシンは…かなり高価なCrayスーパーコンピュータでした。この怪物は を実行しており、rshdワークステーションのユーザーは を実行しrsh、接続してスーパーコンピュータにコマンドを発行することができました。

トムは次のように説明しています。

1990年代半ば、アメリカ最大級の計算センターの一つでのことでした。当時、クレイ・リサーチ社はガリウムヒ素や液体冷却などをベースとしたベクターマシンでスーパーコンピューティングのリーダー的存在でした。一言で言えば、スーパーコンピュータの運用コストはとてつもなく高かったのです。私の最初の仕事は、TCL/TKを学び、どのプログラムが大型スーパーコンピュータでどれだけの計算能力を使っているかを把握するためのグラフィカルなレポートツールを開発することでした。

バム:2日後、ツールが完成し、先月のUnicos(CrayのUnix)レポートデータに対して実行してみました。すると、システムの月間コンピューティングパワーの50%を消費する最大の消費マシンはrshdであることが判明しました。

システム管理者と頭を悩ませた末、rshdこれほどの電力を消費するはずがないと結論付けました。ところが、ある若手開発者が、自分のローカルワークステーションで以下のような計算ループを実装したのです。

(X=0; X < MAX; X++) の場合
{ system("rsh スーパーコンピュータ:./iteration");
}

MAXは数千万単位の規模で、./iteration計算時間はわずか20ミリ秒のアルゴリズムです。呼び出しプログラムは毎回ログインに20秒かかっていましたが、実際にはわずか20ミリ秒しか処理が完了できませんでした。

この男が、このスーパーコンピュータを 1 か月間、あるいは自分のワークステーションだけで完全に稼動させるのではなく、rsh スパム攻撃を仕掛けるのがよい考えだと思った理由は、いまだに私には謎です。

しかし、待ってください。まだ続きがあります。

もう一つの話題は、同じ大規模計算センターでのことでした。同僚の一人が、スーパーコンピュータを使った計算結果が一部矛盾しているという問題を抱えていました。これは、一部のテストデータセットでごく稀に発生していました。当時、私たちは皆、sendmailEmacsとXEmacsを使ってメールの送受信を行うため、中央システムに接続していました。スーパーコンピュータもこのインスタンスに接続されていましたsendmail

それで、案の定、問題をデバッグするために、私の友人は次のようにスーパー上のプログラムを更新しました。

(X=0; X < MAX; X++) の場合
{ 計算(); if(特別な条件) { システム("cat /tmp/data | mailx -s ERROR [email protected]"); }
}

special_conditionテストデータセットで何百万回もの反復処理を行った結果、たった2、3回しか発生しなかったのに、一体何が問題なのでしょう?テストが完了した後、プログラムは実データを使って実行されました。そして、ご想像の通り、実データでは、何百万回もの反復処理の全てspecial_conditionにおいて、このエラーが発生していました。

その日は、電子メールの煩わしさから解放され、楽しい午後を過ごしました。

おそらく、sendmailkill する必要があったからでしょう。これは の標準的な動作モードであるべきですsendmail。ありがとう、トム。あなたは自分が誰なのか分かっているでしょう。教訓:オーバーヘッドに注意してください。ほんの一瞬で終わるコードをキューに入れて数秒を無駄にしないでください。また、コードのデバッグ中はエラーログに何らかのレート制限を設けてください。そうしないと、情報過多に陥ってしまいます。

ぜひあなたの物語(必要であればPGPの詳細も)をお送りください。または、下のコメント欄に投稿してください。来週もこの時間に「garbage in, garbage out」をお楽しみください。®

Discover More