Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB 2.4.8 capped collection and tailable cursor consuming all memory

We are currently exploring Capped Collections and Tailable Cursors within MongoDB to create a queueing system for notifications. However, after creating a simple LinqPad test (code below) we noticed when running, Mongo constantly allocates memory until there are no more resources available, even though we are not inserting any records. This allocation continues until all system RAM is used, at which point Mongo simply stops responding.

As we are new to Capped Collections and Tailable Cursors, I wanted to ensure we havent missed something obvious before submitting a bug.

Note: We tried the code below with journaling on and off with the same results.

  • Platform: Windows Server 2012 64bit
  • MongoDB: Version 2.4.8 64bit
  • Driver: Official C# 10gen v1.8.3.9

Linqpad script

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;
    }
}
like image 758
Needleski Avatar asked Dec 20 '13 09:12

Needleski


People also ask

How do I remove documents from capped collection in MongoDB?

You cannot delete documents from a capped collection. It can only be deleted automatically upon insertion of new documents when the allocated size to the collection has been exhausted. After reading the documents from a capped collection, MongoDB returns the same document in the order which they were present on disk.

What is a Tailable cursor in MongoDB?

Give Feedback. By default, MongoDB will automatically close a cursor when the client has exhausted all results in the cursor. However, for capped collections you may use a Tailable Cursor that remains open after the client exhausts the results in the initial cursor.

What is capped and uncapped collection in MongoDB?

Capped collections are fixed-size collections means when we create the collection, we must fix the maximum size of the collection(in bytes) and the maximum number of documents that it can store.

What is use of capped collection in MongoDB?

Capped collections guarantee preservation of the insertion order. As a result, queries do not need an index to return documents in insertion order. Without this indexing overhead, capped collections can support higher insertion throughput.


1 Answers

It seems I found the solution to the problem!!

The issue in the above code revolves around the query:

Query.EQ("PROCESSED", false)

When I removed this and replaced it with a query based on the id of the document, the memory consumption problem disappeared. On further reflection, this "PROCESSED" property really isnt required in the query as cursor.MoveNext() will always return the next new document (if there is one). Heres the refactored LinqPad script based on the above code....

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;
    }
}
like image 113
Needleski Avatar answered Sep 28 '22 22:09

Needleski