3

这是一个令人费解的问题,甚至难以命名,更不用说描述了。我将从基本事实开始,然后给出可能相关的背景信息。

考虑两个 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'>. 显然 aDBRef没有bar_func属性,但为什么DBRef要返回 a 而不是 的实例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.DBRefbson.dbref.DBRef__dict__

注意:下面为了方便起见,我将调用返回的类型type(foo.bar) fooDBRef,以区别于bson.dbref.DBRef.

为了进一步调试,我修改了DBRef代码以添加一个元类,该元类在创建类型时检查系统模块DBRef,并将这些模块的 ID 列表存储在一个额外的类属性中DBRef。结果表明,创建时存在fooDBRef的模块集与创建裸类型时存在的模块集完全不同。bson.dbref.DBRef一个的所有模块 ID 都不同于另一个的所有模块 ID。

一些可能的相关因素:

  • 发生此错误的服务器在 Apache 下运行 mod_wsgi。
  • 服务器在 wsgi 下运行两个不同的 Django 站点(调用它们site_asite_b)。
  • Foo 定义在 中site_a.foo_app.models,Bar 定义在site_b.bar_app.models.
  • site_asettings.pysite_b.bar_appINSTALLED_APPS.
  • 产生错误的请求由site_a.
  • 创建时有site_b.*模块,但没有模块。反之亦然。sys.modulesfooDBRefsite_a.*bson.dbref.DBRef
  • 错误httpd reload有时会消失一段时间,然后在 0-10 次尝试后返回。

任何人都可以帮我找出导致fooDBRef不同的原因bson.dbref.DBRef吗?

4

1 回答 1

4

您使用的是 mod_wsgi 的嵌入式模式还是守护程序模式?如果使用 mod_wsgi 的守护进程模式,您是否将每个站点委托给不同的守护进程组,然后又强制应用程序在主 Python 解释器中运行?

mongodb Python 客户端模块在 Python 子解释器中可能无法正常工作,尤其是在同一进程的不同子解释器中同时使用该模块时。

因此,您可能必须使用 WSGIDaemonProcess/WSGIProcessGroup 在单独的守护进程组中运行每个站点,然后强制 Python 主解释器的用户使用参数为“%{GLOBAL}”的 WSGIApplicationGroup。

请注意,当对两个站点强制使用主解释器时,您不能再使用嵌入式模式,或者让它们都在同一个守护进程组中运行。因此,为什么您需要强制每个进程在单独的守护进程组中运行。

于 2011-12-08T01:12:27.810 回答