7

現在、MongoDB 内で Capped Collections と Tailable Cursors を調査して、通知用のキューイング システムを作成しています。ただし、単純な LinqPad テスト (以下のコード) を作成した後、実行時に気づいたのですが、Mongo は、レコードを挿入していなくても、使用可能なリソースがなくなるまでメモリを常に割り当てます。この割り当ては、すべてのシステム RAM が使用されるまで続き、その時点で Mongo は単に応答を停止します。

Capped Collections と Tailable Cursor は初めてなので、バグを送信する前に、何か明らかなことを見落としていないことを確認したかったのです。

注: 以下のコードをジャーナリングをオンまたはオフにして試してみましたが、結果は同じでした。

  • プラットフォーム: Windows Server 2012 64bit
  • MongoDB : バージョン 2.4.8 64bit
  • ドライバー: 公式 C# 10gen v1.8.3.9

Linqpad スクリプト

var conn = new MongoClient("mongodb://the.server.url").GetServer().GetDatabase("TestDB");

if(!conn.CollectionExists("Queue")) {

    conn.CreateCollection("Queue", CollectionOptions
        .SetCapped(true)
        .SetMaxSize(100000)
        .SetMaxDocuments(100)
    );

    //Insert an empty document as without this 'cursor.IsDead' is always true
    var coll = conn.GetCollection("Queue");
    coll.Insert(
        new BsonDocument(new Dictionary<string, object> {
            { "PROCESSED", true },
        }), WriteConcern.Unacknowledged
    );
}

var coll = conn.GetCollection("Queue");
var query = coll.Find(Query.EQ("PROCESSED", false))
    .SetFlags(QueryFlags.AwaitData | QueryFlags.NoCursorTimeout | QueryFlags.TailableCursor);

var cursor = new MongoCursorEnumerator<BsonDocument>(query);

while(true) {
    if(cursor.MoveNext()) {
        string.Format(
            "{0:yyyy-MM-dd HH:mm:ss} - {1}",
            cursor.Current["Date"].ToUniversalTime(),
            cursor.Current["X"].AsString
        ).Dump();

        coll.Update(
            Query.EQ("_id", cursor.Current["_id"]),
            Update.Set("PROCESSED", true),
            WriteConcern.Unacknowledged
        );
    } else if(cursor.IsDead) {
        "DONE".Dump();
        break;
    }
}
4

2 に答える 2

5

問題の解決策を見つけたようです!!

上記のコードの問題は、次のクエリに関係しています。

Query.EQ("PROCESSED", false)

これを削除して、ドキュメントの ID に基づくクエリに置き換えると、メモリ消費の問題はなくなりました。さらに考えてみると、cursor.MoveNext() は常に次の新しいドキュメント (存在する場合) を返すため、この "PROCESSED" プロパティはクエリで実際には必要ありません。上記のコードに基づいてリファクタリングされた LinqPad スクリプトを次に示します。

var conn = new MongoClient("mongodb://the.server.url").GetServer().GetDatabase("TestDB");

if(conn.CollectionExists("Queue")) {
    conn.DropCollection("Queue");
}

conn.CreateCollection("Queue", CollectionOptions
    .SetCapped(true)
    .SetMaxSize(100000)
    .SetMaxDocuments(100)
    .SetAutoIndexId(true)
);

//Insert an empty document as without this 'cursor.IsDead' is always true
var coll = conn.GetCollection("Queue");
coll.Insert(
    new BsonDocument(new Dictionary<string, object> {
        { "PROCESSED", true },
        { "Date", DateTime.UtcNow },
        { "X", "test" }
    }), WriteConcern.Unacknowledged
);

//Create query based on latest document id
BsonValue lastId = BsonMinKey.Value;
var query = coll.Find(Query.GT("_id", lastId))
    .SetFlags(QueryFlags.AwaitData | QueryFlags.NoCursorTimeout | QueryFlags.TailableCursor);

var cursor = new MongoCursorEnumerator<BsonDocument>(query);

while(true) {
    if(cursor.MoveNext()) {
        string.Format(
            "{0:yyyy-MM-dd HH:mm:ss} - {1}",
            cursor.Current["Date"].ToUniversalTime(),
            cursor.Current["X"].AsString
        ).Dump();
    } else if(cursor.IsDead) {
        "DONE".Dump();
        break;
    }
}
于 2014-04-01T12:38:32.890 に答える
0

ここでも同じです-追加のクエリなし。

さらに調査した後(実際には非常に多く)、問題は次のようになっていることがわかりました。

最初の MoveNext がレコードを返さない場合、問題が存在します。クエリの種類は問いません。コレクション内のエントリの数は関係ありません。

最後のエントリを最初の結果として返すクエリを変更すると、すべて正常に動作します。これはすでに知っているので、これを破棄してもかまいません...

上の例は、コレクション内のすべてのレコードを最初に取得するため、成功します。

于 2015-10-23T09:21:08.880 に答える