Странность с mongoengine - PullRequest
       9

Странность с mongoengine

3 голосов
/ 08 декабря 2011

Это загадочная проблема, которую трудно даже назвать, не говоря уже о том, чтобы ее описать. Я начну с основных фактов, а затем приведу справочную информацию.

Рассмотрим две модели документов mongoengine:

class Bar(Document):
    # ...
    # field definitions
    # ...
    def bar_func(self):
        pass  # ...or some arbitrary code


class Foo(Document):
    bar = ReferenceField(Bar)

Следующее непоследовательно создает AttributeError на нашем производственном сервере:

# Assume foo_id references a valid Foo document in Mongo
# and that its 'bar' reference is to a valid Bar document.
foo = Foo.objects.with_id(foo_id)
foo.bar.bar_func()  # <-- AttributeError on 'bar_func'

Если я размещу код отладки прямо перед местом ошибки, оценка type(foo.bar) как строки приводит к <class 'bson.dbref.DBRef'>. Очевидно, что DBRef не имеет атрибута bar_func, но почему возвращается DBRef вместо экземпляра Bar?

Дальнейший код отладки показывает, что в функции ReferenceField.__get__ в mongoengine/fields.py не выполняется следующее условие:

if isinstance(value, (pymongo.dbref.DBRef)):
        value = _get_db().dereference(value)

Но (pymongo.dbref.DBRef) на самом деле bson.dbref.DBRef, что похоже на type(foo.bar)! Почему isinstance терпит неудачу?

Вот где все становится действительно странно:

id(type(foo.bar)) == id(bson.dbref.DBRef)  # <-- Evaluates to False!

Другими словами, type(foo.bar) - это , отличное bson.dbref.DBRef от того, которое получено путем прямой ссылки на bson.dbref.DBRef. Фактически, проверка __dict__ этих двух типов показывает различные области памяти для их функций и свойств.

Примечание: Для удобства ниже я назову тип, возвращаемый type(foo.bar) fooDBRef, чтобы отличить его от типа, на который ссылается bson.dbref.DBRef.

Для дальнейшей отладки я изменил код DBRef, добавив метакласс, который проверяет системные модули во время создания типа DBRef и сохраняет список идентификаторов этих модулей в дополнительном атрибуте класса: DBRef. Результаты показывают, что набор модулей, существующих при создании fooDBRef, полностью отличается от набора модулей, существующих при создании типа bson.dbref.DBRef. Все идентификаторы модулей для одного отличаются от всех идентификаторов модулей для другого.

Некоторые, возможно, соответствующие факторы:

  • Сервер, на котором возникает эта ошибка, запускает mod_wsgi под Apache.
  • Сервер запускает два разных сайта Django под wsgi (назовите их site_a и site_b).
  • Foo определено в site_a.foo_app.models, а Bar определено в site_b.bar_app.models.
  • site_a settings.py имеет site_b.bar_app в INSTALLED_APPS.
  • Запросы, которые вызывают ошибку, обрабатываются site_a.
  • При создании fooDBRef в sys.modules было site_b.* модулей, но нет site_a.* модулей. Обратное верно для bson.dbref.DBRef.
  • После httpd reload ошибка иногда исчезает на некоторое время и возвращается через 0-10 попыток.

Может кто-нибудь помочь мне выяснить, чем причина fooDBRef отличается от bson.dbref.DBRef?

1 Ответ

4 голосов
/ 08 декабря 2011

Вы используете встроенный режим или режим демона mod_wsgi? Если вы используете режим демона mod_wsgi, делегируете ли вы каждый сайт другой группе процессов демона, а затем заставляете приложение запускаться в основном интерпретаторе Python?

Может случиться так, что клиентский модуль mongodb Python может не работать должным образом в субинтерпретаторах Python, особенно если модуль одновременно используется в другом субинтерпретаторе того же процесса.

Таким образом, вам может потребоваться запустить каждый сайт в отдельной группе процессов демона, используя WSGIDaemonProcess / WSGIProcessGroup, а затем принудительно заставить пользователя основного интерпретатора Python использовать WSGIApplicationGroup с аргументом «% {GLOBAL}».

Обратите внимание, что при принудительном использовании основного интерпретатора для обоих сайтов вы больше не можете использовать встроенный режим или запускать их обоих в одной группе процессов демона. Таким образом, вам нужно заставить каждый запускаться в отдельной группе процессов демона.

...