Использование хвостовых курсоров pymongo умирает на пустых коллекциях

Надеюсь, кто-то может помочь мне понять, вижу ли я проблему или просто не понимаю поведение хвостового курсора 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)


Это известное поведение, и «решение» с двумя циклами является общепринятой практикой для обхода этого случая. В случае, если коллекция пуста, вместо немедленной повторной попытки и входа в тугой цикл, как вы предлагаете, вы можете ненадолго заснуть (особенно если вы ожидаете, что скоро будут данные для хвоста).

person dcrosta    schedule 25.04.2012
comment
Ух, это отстой. Я сделал это, но это заставило меня чувствовать себя грязным. - person DaGoodBoy; 02.05.2012