2013-12-20 2 views
7

В настоящее время мы изучаем Capped Collections и Tailable Cursors в MongoDB для создания системы массового обслуживания для уведомлений. Однако после создания простого теста LinqPad (код ниже), который мы заметили при запуске, Mongo постоянно выделяет память, пока не будет доступных ресурсов, даже если мы не вставляем никаких записей. Это распределение продолжается до тех пор, пока не будет использовано все системное ОЗУ, после чего Mongo просто перестает отвечать на запросы.MongoDB 2.4.8 capped collection и tailable курсор, потребляющий всю память

Поскольку мы новичок в Capped Collections и Tailable Cursors, я хотел, чтобы мы не пропустили что-то очевидное, прежде чем отправлять ошибку.

Примечание. Мы пробовали код ниже с ведением журнала вкл./Выкл. С теми же результатами.

  • Платформа: Windows Server 2012 64bit
  • MongoDB: Версия 2.4.8 64bit
  • Driver: Официальный 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; 
    } 
} 
+0

Можете ли вы запустить db.currentOp(), пока это запустит сообщение? Можете ли вы также опубликовать свой файл журнала mongod, охватывающий пробег? –

+0

Я столкнулся с той же проблемой ... Кажется, mongoDB держит весь oplog в RAM , пока он работает. Для информации, я использую одну машину, добавляя репликацию только для этой функции. Вот мой currentOp и войти: https://dl.dropboxusercontent.com/u/853035/currentOp.txt https://dl.dropboxusercontent.com/u/853035/log.txt –

+0

Теперь я обнаружил, что использование памяти составляет около 2,4 ГБ, вводя MoveNext в первый раз. После того, как первый документ будет возвращен, он упадет до 1,2 ГБ, после второго документа он вернется к исходному значению ??? Смотрите изображение здесь: https://dl.dropboxusercontent.com/u/853035/memory_usage.png –

ответ

5

Кажется, я нашел решение проблемы!

Проблема в коде выше вращается вокруг запроса:

Query.EQ("PROCESSED", false) 

Когда я снял это и заменить его с помощью запроса на основе идентификатора документа, проблема потребления памяти исчезла. Дальнейшее размышление, это свойство «PROCESSED» действительно не требуется в запросе как cursor.MoveNext() всегда будет возвращать следующий новый документ (если таковой имеется). Heres реорганизованный сценарий 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; 
    } 
} 
0

То же самое здесь - без этого дополнительного запроса.

После еще некоторого исследования (на самом деле гораздо более) я нашел проблема выглядит следующим образом:

Если первый MoveNext не возвращает запись проблема существует. Неважно, какой именно запрос. Неважно, сколько записей в коллекции.

Если вы изменили запрос, возвращающий последнюю запись, как первый результат , все будет хорошо. Вы можете отказаться от этого, так как вы знаете это уже ...

Верхний пример преуспевает, потому что вы получаете изначально ВСЕ записи, уже находящиеся в коллекции.

Смежные вопросы