Използването на pymongo tailable курсори умира при празни колекции

Надявам се, че някой може да ми помогне да разбера дали виждам проблем или просто не разбирам поведението на tailable курсора на mongodb. Работя с mongodb 2.0.4 и pymongo 2.1.1.

Ето един скрипт, който демонстрира проблема.

#!/usr/bin/python

import sys
import time
import pymongo

MONGO_SERVER = "127.0.0.1"
MONGO_DATABASE = "mdatabase"
MONGO_COLLECTION = "mcollection"

mongodb    = pymongo.Connection(MONGO_SERVER, 27017)
database   = mongodb[MONGO_DATABASE]

if MONGO_COLLECTION in database.collection_names():
  database[MONGO_COLLECTION].drop()

print "creating capped collection"
database.create_collection(
  MONGO_COLLECTION,
  size=100000,
  max=100,
  capped=True
)
collection = database[MONGO_COLLECTION]

# Run this script with any parameter to add one record
# to the empty collection and see the code below
# loop correctly
#
if len(sys.argv[1:]):
  collection.insert(
    {
      "key" : "value",
    }
  )

# Get a tailable cursor for our looping fun
cursor = collection.find( {},
                          await_data=True,
                          tailable=True )

# This will catch ctrl-c and the error thrown if
# the collection is deleted while this script is
# running.
try:

  # The cursor should remain alive, but if there
  # is nothing in the collection, it dies after the
  # first loop. Adding a single record will
  # keep the cursor alive forever as I expected.
  while cursor.alive:
    print "Top of the loop"
    try:
      message = cursor.next()
      print message
    except StopIteration:
      print "MongoDB, why you no block on read?!"
      time.sleep(1)

except pymongo.errors.OperationFailure:
  print "Delete the collection while running to see this."

except KeyboardInterrupt:
  print "trl-C Ya!"
  sys.exit(0)

print "and we're out"

# End

Така че, ако погледнете кода, е доста лесно да демонстрирам проблема, който имам. Когато стартирам кода срещу празна колекция (правилно ограничена и готова за проследяване), курсорът умира и кодът ми излиза след един цикъл. Добавянето на първи запис в колекцията го кара да се държи по начина, по който бих очаквал да се държи задният курсор.

Освен това каква е сделката с изключението StopIteration, което убива cursor.next() в очакване на данни? Защо бекендът не може просто да блокира, докато данните станат налични? Предположих, че await_data наистина ще направи нещо, но изглежда само че поддържа връзката да чака секунда или две по-дълго, отколкото без него.

Повечето примери в мрежата показват поставяне на втори цикъл While True около цикъла cursor.alive, но след това, когато скриптът следи празна колекция, цикълът просто се върти и върти, губейки време на процесора за нищо. Наистина не искам да поставям нито един фалшив запис, само за да избегна този проблем при стартиране на приложението.


person DaGoodBoy    schedule 25.04.2012    source източник
comment
безкрайното блокиране на нещо никога не е добра идея   -  person mensi    schedule 25.04.2012
comment
Кажете това на gobject.MainLoop().run() ;)   -  person DaGoodBoy    schedule 25.04.2012


Отговори (1)


Това е известно поведение и „решението“ с 2 цикъла е приетата практика за заобикаляне на този случай. В случай, че колекцията е празна, вместо веднага да опитате отново и да влезете в тесен цикъл, както предлагате, можете да спите за кратко (особено ако очаквате, че скоро ще има данни за опашка).

person dcrosta    schedule 25.04.2012
comment
Уф, това е гадно. Направих го, но ме накара да се почувствам мръсен. - person DaGoodBoy; 02.05.2012